@bolloon/bolloon-agent 0.4.33 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/contacts/chain.js +27 -7
- package/dist/agents/execution-supervisor.js +580 -40
- package/dist/agents/external-events.js +35 -6
- package/dist/agents/gateway-group.js +100 -12
- package/dist/agents/goal-criteria.js +10 -3
- package/dist/agents/goal-flywheel/block-executor.js +346 -0
- package/dist/agents/goal-flywheel/continuation-decision.js +592 -0
- package/dist/agents/goal-flywheel/goal-change.js +754 -0
- package/dist/agents/goal-flywheel/index.js +26 -0
- package/dist/agents/goal-flywheel/memory-layers.js +227 -0
- package/dist/agents/goal-flywheel/run-closure.js +809 -0
- package/dist/agents/goal-flywheel/skill-candidate.js +628 -0
- package/dist/agents/goal-flywheel/types.js +329 -0
- package/dist/agents/goal-flywheel/wiring/change.js +288 -0
- package/dist/agents/goal-flywheel/wiring/closure.js +697 -0
- package/dist/agents/goal-flywheel/wiring/continuation.js +432 -0
- package/dist/agents/goal-flywheel/wiring/contract.js +270 -0
- package/dist/agents/goal-flywheel/wiring/index.js +26 -0
- package/dist/agents/goal-flywheel/wiring/monitor.js +254 -0
- package/dist/agents/goal-flywheel/wiring/seams.js +592 -0
- package/dist/agents/goal-flywheel/work-contract.js +478 -0
- package/dist/agents/goal-flywheel/work-monitor.js +488 -0
- package/dist/agents/goal-flywheel-wiring.js +1870 -0
- package/dist/agents/goal-state-reducer.js +406 -0
- package/dist/agents/goal-store.js +35 -1
- package/dist/agents/mobile-flywheel-view.js +144 -0
- package/dist/agents/mobile-task-actions-verify.js +74 -0
- package/dist/agents/mobile-task-actions.js +178 -0
- package/dist/agents/mobile-task-views.js +199 -0
- package/dist/agents/model-policy.js +651 -0
- package/dist/agents/network-pulse.js +304 -18
- package/dist/agents/pi-sdk-tools.js +84 -33
- package/dist/agents/pi-sdk.js +157 -42
- package/dist/agents/run-store.js +71 -5
- package/dist/agents/skill-readiness.js +52 -10
- package/dist/agents/subagent-manager.js +209 -3
- package/dist/agents/task/task-runner.js +17 -4
- package/dist/agents/task-group.js +8 -51
- package/dist/agents/task-public-text.js +89 -0
- package/dist/agents/workflow-pivot-loop.js +19 -12
- package/dist/cli/clipboard.js +65 -0
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/tasks.js +350 -2
- package/dist/cli/identity-command.js +177 -0
- package/dist/cli/ink-app.js +145 -17
- package/dist/cli/log-gate.js +386 -0
- package/dist/cli/mention-data.js +2 -0
- package/dist/cli/model-selector.js +538 -0
- package/dist/cli/reply-hygiene.js +54 -0
- package/dist/cli/setup-wizard.js +757 -69
- package/dist/cli/startup-notice.js +160 -0
- package/dist/cli/stores.js +22 -1
- package/dist/cli/theme.js +49 -0
- package/dist/cli/tui-select.js +970 -0
- package/dist/cli/update-commands.js +150 -24
- package/dist/cli-entry.js +137 -20
- package/dist/electron-build/agents/contacts/chain.js +26 -6
- package/dist/electron-build/agents/contacts/chain.js.map +1 -1
- package/dist/electron-build/agents/execution-supervisor.js +578 -38
- package/dist/electron-build/agents/execution-supervisor.js.map +1 -1
- package/dist/electron-build/agents/external-events.js +34 -5
- package/dist/electron-build/agents/external-events.js.map +1 -1
- package/dist/electron-build/agents/goal-criteria.js +10 -3
- package/dist/electron-build/agents/goal-criteria.js.map +1 -1
- package/dist/electron-build/agents/goal-flywheel/continuation-decision.js +598 -0
- package/dist/electron-build/agents/goal-flywheel/continuation-decision.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/goal-change.js +773 -0
- package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/memory-layers.js +269 -0
- package/dist/electron-build/agents/goal-flywheel/memory-layers.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/run-closure.js +818 -0
- package/dist/electron-build/agents/goal-flywheel/run-closure.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/skill-candidate.js +642 -0
- package/dist/electron-build/agents/goal-flywheel/skill-candidate.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/types.js +333 -0
- package/dist/electron-build/agents/goal-flywheel/types.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/change.js +294 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/change.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/closure.js +742 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/closure.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/continuation.js +444 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/continuation.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/contract.js +279 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/contract.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/index.js +111 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/index.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/monitor.js +264 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/monitor.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/seams.js +612 -0
- package/dist/electron-build/agents/goal-flywheel/wiring/seams.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/work-contract.js +488 -0
- package/dist/electron-build/agents/goal-flywheel/work-contract.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel/work-monitor.js +501 -0
- package/dist/electron-build/agents/goal-flywheel/work-monitor.js.map +1 -0
- package/dist/electron-build/agents/goal-flywheel-wiring.js +1962 -0
- package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -0
- package/dist/electron-build/agents/goal-state-reducer.js +414 -0
- package/dist/electron-build/agents/goal-state-reducer.js.map +1 -0
- package/dist/electron-build/agents/goal-store.js +35 -1
- package/dist/electron-build/agents/goal-store.js.map +1 -1
- package/dist/electron-build/agents/model-policy.js +706 -0
- package/dist/electron-build/agents/model-policy.js.map +1 -0
- package/dist/electron-build/agents/network-pulse.js +307 -19
- package/dist/electron-build/agents/network-pulse.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-tools.js +84 -33
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +156 -41
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/run-store.js +75 -6
- package/dist/electron-build/agents/run-store.js.map +1 -1
- package/dist/electron-build/agents/skill-readiness.js +52 -9
- package/dist/electron-build/agents/skill-readiness.js.map +1 -1
- package/dist/electron-build/agents/task/task-runner.js +16 -3
- package/dist/electron-build/agents/task/task-runner.js.map +1 -1
- package/dist/electron-build/agents/workflow-pivot-loop.js +19 -12
- package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
- package/dist/electron-build/llm/config-store.js +146 -6
- package/dist/electron-build/llm/config-store.js.map +1 -1
- package/dist/electron-build/llm/connection-probe.js +764 -0
- package/dist/electron-build/llm/connection-probe.js.map +1 -0
- package/dist/electron-build/llm/custom-provider-store.js +272 -0
- package/dist/electron-build/llm/custom-provider-store.js.map +1 -0
- package/dist/electron-build/llm/data/provider-catalog-baked.js +23 -0
- package/dist/electron-build/llm/data/provider-catalog-baked.js.map +1 -0
- package/dist/electron-build/llm/model-catalog.js +778 -0
- package/dist/electron-build/llm/model-catalog.js.map +1 -0
- package/dist/electron-build/llm/model-discovery.js +1202 -0
- package/dist/electron-build/llm/model-discovery.js.map +1 -0
- package/dist/electron-build/llm/model-selection.js +1315 -0
- package/dist/electron-build/llm/model-selection.js.map +1 -0
- package/dist/electron-build/llm/pi-ai.js +70 -11
- package/dist/electron-build/llm/pi-ai.js.map +1 -1
- package/dist/electron-build/llm/provider-catalog.js +969 -0
- package/dist/electron-build/llm/provider-catalog.js.map +1 -0
- package/dist/electron-build/llm/provider-registry.js +950 -0
- package/dist/electron-build/llm/provider-registry.js.map +1 -0
- package/dist/electron-build/llm/tool-name.js +266 -0
- package/dist/electron-build/llm/tool-name.js.map +1 -0
- package/dist/electron-build/orbitdb/cid-database.js +83 -17
- package/dist/electron-build/orbitdb/cid-database.js.map +1 -1
- package/dist/electron-build/orbitdb/ipfs-node.js +31 -2
- package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -1
- package/dist/electron-build/utils/dual-source-facts.js +236 -0
- package/dist/electron-build/utils/dual-source-facts.js.map +1 -0
- package/dist/electron-build/utils/dual-source.js +347 -0
- package/dist/electron-build/utils/dual-source.js.map +1 -0
- package/dist/electron-build/utils/update-manager.js +328 -104
- package/dist/electron-build/utils/update-manager.js.map +1 -1
- package/dist/electron-build/utils/update-state.js +9 -11
- package/dist/electron-build/utils/update-state.js.map +1 -1
- package/dist/electron-build/utils/version-identity.js +193 -0
- package/dist/electron-build/utils/version-identity.js.map +1 -0
- package/dist/electron-build/utils/version-info.js +57 -31
- package/dist/electron-build/utils/version-info.js.map +1 -1
- package/dist/index.js +385 -108
- package/dist/ios/bolloon-web.json +10 -0
- package/dist/ios/chain-explorer.js +617 -0
- package/dist/ios/explorer.html +163 -0
- package/dist/ios/index.html +96 -0
- package/dist/ios/mobile-agent.js +2 -2
- package/dist/ios/mobile-contacts.js +362 -0
- package/dist/ios/mobile-core.js +3102 -310
- package/dist/ios/mobile-privacy.js +185 -0
- package/dist/ios/mobile-tasks.js +339 -0
- package/dist/ios/mobile-update.js +1442 -0
- package/dist/ios/mobile.css +19 -0
- package/dist/ios/mobile.html +96 -0
- package/dist/ios/mobile.js +1013 -41
- package/dist/ios/routes-chain-index.js +108 -0
- package/dist/ios/routes-contacts.js +420 -0
- package/dist/ios/routes-llm-config.js +344 -52
- package/dist/ios/routes-mobile-tasks.js +353 -0
- package/dist/ios/routes-onchain-trade.js +99 -0
- package/dist/ios/routes-x402-info.js +1 -0
- package/dist/ios/server.js +921 -0
- package/dist/ios/sw.js +1 -1
- package/dist/ios/task-frame-server.js +211 -0
- package/dist/llm/config-store.js +146 -6
- package/dist/llm/connection-probe.js +744 -0
- package/dist/llm/custom-provider-store.js +224 -0
- package/dist/llm/data/provider-catalog-baked.js +19 -0
- package/dist/llm/model-catalog.js +717 -0
- package/dist/llm/model-discovery.js +1137 -0
- package/dist/llm/model-selection.js +1241 -0
- package/dist/llm/pi-ai.js +70 -11
- package/dist/llm/provider-catalog.js +898 -0
- package/dist/llm/provider-registry.js +917 -0
- package/dist/llm/tool-name.js +220 -0
- package/dist/orbitdb/cid-database.js +81 -16
- package/dist/orbitdb/ipfs-node.js +31 -2
- package/dist/setup/onboard.js +34 -8
- package/dist/utils/dual-source-facts.js +223 -0
- package/dist/utils/dual-source.js +297 -0
- package/dist/utils/update-cli.js +16 -21
- package/dist/utils/update-health.js +35 -5
- package/dist/utils/update-manager.js +325 -96
- package/dist/utils/update-state.js +13 -10
- package/dist/utils/version-identity.js +176 -0
- package/dist/utils/version-info.js +27 -22
- package/dist/web/bolloon-web.json +10 -0
- package/dist/web/mobile-contacts.js +44 -11
- package/dist/web/mobile-core.js +2901 -580
- package/dist/web/mobile-tasks.js +339 -0
- package/dist/web/mobile-update.js +1442 -0
- package/dist/web/mobile.css +19 -0
- package/dist/web/mobile.html +64 -0
- package/dist/web/mobile.js +838 -35
- package/dist/web/routes-llm-config.js +344 -52
- package/dist/web/routes-mobile-tasks.js +353 -0
- package/dist/web/server.js +163 -4
- package/dist/web/sw.js +1 -1
- package/package.json +3 -1
- package/scripts/build-web.ts +36 -0
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* 2 检查不可用 (离线 / registry 不可用 / 读不到本地版本) —— 与"没有更新"区分开
|
|
14
14
|
*/
|
|
15
15
|
import { resolveBolloonHome } from '../setup/setup-store.js';
|
|
16
|
-
import { collectVersionInfo, renderVersionText, renderVersionJson, } from '../utils/version-info.js';
|
|
17
|
-
import { checkForUpdate, buildUpdatePlan, renderUpdatePlan, applyUpdate, readUpdateStatus, readHistory, renderHistory, updateSummaryFor, compareVersions, } from '../utils/update-manager.js';
|
|
16
|
+
import { collectVersionInfo, renderVersionText, renderVersionJson, DEV_CHANNEL_WARNING, DEV_BACK_TO_STABLE_HINT, channelKindOf, isDevIdentity, } from '../utils/version-info.js';
|
|
17
|
+
import { checkForUpdate, buildUpdatePlan, renderUpdatePlan, applyUpdate, readUpdateStatus, readHistory, renderHistory, updateSummaryFor, compareVersions, checkExitCode, REFUSED_STATUSES, } from '../utils/update-manager.js';
|
|
18
|
+
import { renderGithubReportLine } from '../utils/dual-source.js';
|
|
18
19
|
import { runDoctor, renderDoctor } from '../utils/update-health.js';
|
|
19
20
|
import { detectRuntimeReport, bootstrapRuntimes, renderRuntimeReport, renderBootstrapPlan, RUNTIME_MIN, readRuntimeConfigWithFallback, } from '../utils/runtime-bootstrap.js';
|
|
20
21
|
const RESET = '\x1b[0m';
|
|
@@ -25,6 +26,45 @@ const YELLOW = '\x1b[38;2;245;158;11m';
|
|
|
25
26
|
const RED = '\x1b[38;2;239;68;68m';
|
|
26
27
|
const out = (s) => process.stdout.write(s + '\n');
|
|
27
28
|
const err = (s) => process.stderr.write(s + '\n');
|
|
29
|
+
// ── --channel (§12.2: 只覆盖本次进程, 不落盘) ───────────────────────────────
|
|
30
|
+
/** 允许的通道字面量 (`--channel stable|dev`; beta 仍是"与 stable 同源"的老口径)。 */
|
|
31
|
+
const CHANNEL_VALUES = ['stable', 'dev', 'beta'];
|
|
32
|
+
/**
|
|
33
|
+
* 解析 `--channel <v>` / `--channel=<v>`。
|
|
34
|
+
* 给了不认识的值 → **拒绝并说清** (不静默落回 stable, 那会让人以为切成功了)。
|
|
35
|
+
*/
|
|
36
|
+
export function parseChannelArg(args) {
|
|
37
|
+
let raw = null;
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
const a = args[i];
|
|
40
|
+
if (a === '--channel') {
|
|
41
|
+
raw = args[i + 1] ?? '';
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
if (a.startsWith('--channel=')) {
|
|
45
|
+
raw = a.slice('--channel='.length);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (a === 'channel') {
|
|
49
|
+
raw = args[i + 1] ?? '';
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (raw === null)
|
|
54
|
+
return { given: false };
|
|
55
|
+
const v = raw.trim().toLowerCase();
|
|
56
|
+
if (!CHANNEL_VALUES.includes(v)) {
|
|
57
|
+
return { given: true, error: `--channel 只接受 ${CHANNEL_VALUES.join('|')}, 收到 "${raw}" — 拒绝执行 (没有静默落回 stable)` };
|
|
58
|
+
}
|
|
59
|
+
return { channel: v, given: true };
|
|
60
|
+
}
|
|
61
|
+
/** dev 通道的三条硬约束 (§12.4) 在这里统一打印: 显式警告 + 记录 sha + 一键回 stable。 */
|
|
62
|
+
function printDevNotice() {
|
|
63
|
+
out('');
|
|
64
|
+
out(DEV_CHANNEL_WARNING);
|
|
65
|
+
out(DEV_BACK_TO_STABLE_HINT);
|
|
66
|
+
out('');
|
|
67
|
+
}
|
|
28
68
|
// ── 版本 ────────────────────────────────────────────────────────────────────
|
|
29
69
|
export async function runVersionCommand(args = []) {
|
|
30
70
|
// 2026-09-19 (leo): 子命令一律**裸词**, 不再带 `--` 前缀 (只有 `--version` 本身保留 --)
|
|
@@ -78,22 +118,39 @@ function statusConclusion(status, latest, current) {
|
|
|
78
118
|
const cmp = !latest ? '' : compareVersions(current, latest) < 0 ? ` — 已有更新版 ${latest}, 需按该安装方式手动升级` : ` — 本地 ${current} 已是 registry 上最新`;
|
|
79
119
|
return `${YELLOW}当前安装方式不支持自动更新${RESET}${cmp}`;
|
|
80
120
|
}
|
|
121
|
+
case 'github_unavailable': return `${RED}GitHub 源不可用${RESET} — ${BOLD}不能判定为最新${RESET}`;
|
|
122
|
+
case 'cross_check_mismatch': return `${RED}两个源不一致${RESET} — npm 与 GitHub 指向不同版本, ${BOLD}拒绝更新${RESET}`;
|
|
81
123
|
default: return '尚未检查';
|
|
82
124
|
}
|
|
83
125
|
}
|
|
84
126
|
export function renderCheckResult(r) {
|
|
127
|
+
const dev = r.channelKind === 'git-ref';
|
|
85
128
|
const L = [];
|
|
86
|
-
L.push(`当前版本: ${r.currentVersion}`);
|
|
87
|
-
L.push(
|
|
88
|
-
L.push(`更新通道: ${r.channel} (
|
|
129
|
+
L.push(`当前版本: ${r.currentVersion}${r.installedDevSha ? ` (dev 快照 commit ${r.installedDevSha})` : ''}`);
|
|
130
|
+
L.push(`${dev ? '最新快照' : '最新版本'}: ${r.latestVersion || '未知'}`);
|
|
131
|
+
L.push(`更新通道: ${r.channel} (比较语义: ${r.channelKind || channelKindOf(r.channel)})`);
|
|
132
|
+
L.push(`当前来源: ${r.installedChannel === 'dev' ? `dev (GitHub master 快照 @ ${r.installedDevSha})` : 'stable (npm registry)'}`);
|
|
89
133
|
L.push(`安装方式: ${r.installMethod}`);
|
|
134
|
+
if (dev)
|
|
135
|
+
L.push(renderGithubReportLine(r.github));
|
|
136
|
+
else {
|
|
137
|
+
L.push(renderGithubReportLine(r.github));
|
|
138
|
+
if (r.crossCheck)
|
|
139
|
+
L.push(`交叉校验: ${r.crossCheck.kind}${r.crossCheck.blocking ? ' (阻塞)' : ''} — ${r.crossCheck.detail}`);
|
|
140
|
+
}
|
|
141
|
+
if (r.dev)
|
|
142
|
+
L.push(`dev 判定: ${r.dev.detail}`);
|
|
143
|
+
if (r.switchableTo)
|
|
144
|
+
L.push(`能切回: ${r.switchableTo.channel} (${r.switchableTo.source}${r.switchableTo.target ? ` @ ${r.switchableTo.target}` : ''})`);
|
|
90
145
|
L.push(`检查时间: ${r.checkedAt}${r.fromCache ? ' (缓存)' : ''}`);
|
|
91
146
|
L.push(`结论: ${statusConclusion(r.status, r.latestVersion, r.currentVersion)}`);
|
|
92
147
|
if (r.reason)
|
|
93
148
|
L.push(`说明: ${r.reason}`);
|
|
94
149
|
L.push('');
|
|
95
150
|
if (r.status === 'update_available') {
|
|
96
|
-
L.push(`下一步: ${BOLD}bolloon update plan${RESET} 看计划, ${BOLD}bolloon update now${RESET} 执行更新`);
|
|
151
|
+
L.push(`下一步: ${BOLD}bolloon update plan --channel ${r.channel}${RESET} 看计划, ${BOLD}bolloon update now --channel ${r.channel}${RESET} 执行更新`);
|
|
152
|
+
if (dev)
|
|
153
|
+
L.push(DEV_CHANNEL_WARNING), L.push(DEV_BACK_TO_STABLE_HINT);
|
|
97
154
|
}
|
|
98
155
|
else if (r.status === 'unsupported_installation') {
|
|
99
156
|
L.push('下一步: 按上面"说明"里对应安装方式更新 (源码目录: git pull && npm install && npm run build:all)');
|
|
@@ -101,6 +158,14 @@ export function renderCheckResult(r) {
|
|
|
101
158
|
else if (r.status === 'offline' || r.status === 'registry_unavailable') {
|
|
102
159
|
L.push('下一步: 检查网络/代理后重跑 `bolloon update`; 也可以 `bolloon update status` 看上次结论');
|
|
103
160
|
}
|
|
161
|
+
else if (r.status === 'github_unavailable') {
|
|
162
|
+
L.push('下一步: 检查到 GitHub 的网络/代理/限流后重跑 `bolloon update --channel dev`;'
|
|
163
|
+
+ `${BOLD}dev 通道不会退回 stable 装旧版${RESET} —— 要稳定版请显式 --channel stable`);
|
|
164
|
+
}
|
|
165
|
+
else if (r.status === 'cross_check_mismatch') {
|
|
166
|
+
L.push('下一步: 两个发布源不一致, 先查清哪个是对的 (npm: registry dist-tags / GitHub: Release 记录);'
|
|
167
|
+
+ '本条不执行任何安装, 也不会按 GitHub 的 tag 去装。');
|
|
168
|
+
}
|
|
104
169
|
else if (r.status === 'local_version_unknown') {
|
|
105
170
|
L.push('下一步: 重新安装 (npm install -g @bolloon/bolloon-agent) 以恢复 package.json');
|
|
106
171
|
}
|
|
@@ -109,32 +174,63 @@ export function renderCheckResult(r) {
|
|
|
109
174
|
export function renderStatusReport(s) {
|
|
110
175
|
const L = [];
|
|
111
176
|
const lu = s.lastUpdate;
|
|
177
|
+
const dev = s.installedChannel === 'dev';
|
|
112
178
|
L.push(`${BOLD}更新状态${RESET}`);
|
|
113
179
|
L.push(`当前版本: ${s.currentVersion}`);
|
|
180
|
+
// ── §12.5: 必须说清"当前装的是哪个源 + 哪个版本/commit sha + 能切回哪个源" ──
|
|
181
|
+
L.push(`安装来源: ${dev
|
|
182
|
+
? `dev (GitHub master 快照, ref ${s.devRef || 'refs/heads/master'}, commit ${s.installedDevSha || '未知'})`
|
|
183
|
+
: 'stable (npm registry)'}`);
|
|
184
|
+
L.push(`身份标识: ${dev ? `commit ${s.installedDevSha || '未知'}` : s.currentVersion}`);
|
|
185
|
+
L.push(`能切回: ${s.switchableTo
|
|
186
|
+
? `${s.switchableTo.channel} (${s.switchableTo.source}${s.switchableTo.target ? ` @ ${s.switchableTo.target}` : ''}) — 一键切: bolloon update now --channel ${s.switchableTo.channel}`
|
|
187
|
+
: '无 (未知来源)'}`);
|
|
188
|
+
if (s.lastDevSha && !dev)
|
|
189
|
+
L.push(`上次 dev: commit ${s.lastDevSha}${s.devCheckedAt ? ` @ ${s.devCheckedAt}` : ''} (已切回 stable)`);
|
|
114
190
|
L.push(`最新版本: ${s.latestVersion || (s.lastCheckStatus ? '未知' : '尚未检查')}`);
|
|
115
191
|
L.push(`是否最新: ${statusConclusion(s.lastCheckStatus, s.latestVersion, s.currentVersion)}`);
|
|
116
|
-
L.push(`更新通道: ${s.channel}`);
|
|
192
|
+
L.push(`更新通道: ${s.channel} (比较语义: ${s.channelKind})`);
|
|
117
193
|
L.push(`安装方式: ${s.installMethod}`);
|
|
118
194
|
L.push(`安装目录: ${s.installDir}`);
|
|
119
195
|
L.push(`运行入口: ${s.entryPath}`);
|
|
196
|
+
if (s.sourceFacts) {
|
|
197
|
+
const f = s.sourceFacts;
|
|
198
|
+
L.push(`源事实: npm ${f.npm?.reachable ? `可达 (latest=${f.npm.latest || '未知'})` : `不可达${f.npm?.detail ? ` (${f.npm.detail})` : ''}`}`
|
|
199
|
+
+ ` · GitHub ${f.github?.reachable
|
|
200
|
+
? `可达 (HEAD ${f.github.headSha ? f.github.headSha.slice(0, 7) : '未知'}, Release ${f.github.releases ?? 0}, Tag ${f.github.tags ?? 0})`
|
|
201
|
+
: `不可达${f.github?.reason ? ` (github_unavailable(${f.github.reason}))` : ' (未查询)'}`}`);
|
|
202
|
+
if (f.crossCheck)
|
|
203
|
+
L.push(`交叉校验: ${f.crossCheck.kind}${f.crossCheck.blocking ? ' (阻塞)' : ''} — ${f.crossCheck.detail}`);
|
|
204
|
+
}
|
|
120
205
|
L.push(`最近检查: ${s.lastCheckAt || '从未'}${s.lastCheckReason ? ` (${s.lastCheckReason})` : ''}`);
|
|
121
206
|
L.push(`最近更新: ${lu ? `${lu.at} ${lu.from} → ${lu.to} [${lu.status}]${lu.reason ? ` ${lu.reason}` : ''}` : '无记录'}`);
|
|
122
207
|
L.push(`最近失败: ${s.lastFailure ? `${s.lastFailure.at} @${s.lastFailure.stage}: ${s.lastFailure.reason}` : '无'}`);
|
|
123
208
|
L.push(`需要重启: ${s.needsRestart ? '是 (新版已就位, 重启后生效)' : '否'}`);
|
|
124
209
|
L.push(`更新锁: ${s.lock ? `有 (pid ${s.lock.pid}, ${s.lock.at}${s.lock.stale ? ', 已陈旧' : ''})` : '无'}`);
|
|
125
210
|
L.push(`开关: 检查更新=${s.prefs.checkUpdates} (${s.prefs.sources.checkUpdates}) · 自动安装=${s.prefs.autoInstall} (${s.prefs.sources.autoInstall}) · 自动重启=${s.prefs.autoRestart} (${s.prefs.sources.autoRestart})`);
|
|
211
|
+
if (dev) {
|
|
212
|
+
L.push('');
|
|
213
|
+
L.push(DEV_CHANNEL_WARNING);
|
|
214
|
+
L.push(DEV_BACK_TO_STABLE_HINT);
|
|
215
|
+
}
|
|
126
216
|
return L.join('\n');
|
|
127
217
|
}
|
|
128
218
|
// ── update 子命令 ───────────────────────────────────────────────────────────
|
|
129
219
|
export async function runUpdateCommand(args = []) {
|
|
130
220
|
const home = resolveBolloonHome();
|
|
131
|
-
// 裸词优先 (bolloon update plan / status / history / now / wait / force / json),
|
|
221
|
+
// 裸词优先 (bolloon update plan / status / history / now / wait / force / json / --channel dev),
|
|
132
222
|
// 同时兼容旧的 --plan / --status / ... 写法 (已有脚本不断裂)。
|
|
133
223
|
const has = (...f) => f.some((x) => args.includes(x));
|
|
134
224
|
const json = has('json', '--json');
|
|
225
|
+
const ch = parseChannelArg(args);
|
|
226
|
+
if (ch.error) {
|
|
227
|
+
err(`${RED}✗ ${ch.error}${RESET}`);
|
|
228
|
+
return 2;
|
|
229
|
+
}
|
|
230
|
+
const channel = ch.channel;
|
|
135
231
|
// ── 状态 ──
|
|
136
232
|
if (has('status', '--status')) {
|
|
137
|
-
const s = await readUpdateStatus({ home });
|
|
233
|
+
const s = await readUpdateStatus({ home, channel });
|
|
138
234
|
if (json) {
|
|
139
235
|
out(JSON.stringify(s, null, 2));
|
|
140
236
|
return 0;
|
|
@@ -156,21 +252,26 @@ export async function runUpdateCommand(args = []) {
|
|
|
156
252
|
}
|
|
157
253
|
// ── 计划 ──
|
|
158
254
|
if (has('plan', '--plan')) {
|
|
159
|
-
const plan = await buildUpdatePlan({ home, force: true });
|
|
255
|
+
const plan = await buildUpdatePlan({ home, force: true, channel });
|
|
160
256
|
if (json) {
|
|
161
257
|
out(JSON.stringify(plan, null, 2));
|
|
162
258
|
return 0;
|
|
163
259
|
}
|
|
164
260
|
out(renderUpdatePlan(plan));
|
|
261
|
+
if (plan.channelKind === 'git-ref')
|
|
262
|
+
printDevNotice();
|
|
165
263
|
return 0;
|
|
166
264
|
}
|
|
167
265
|
// ── 执行 ──
|
|
168
266
|
if (has('now', '--now')) {
|
|
169
267
|
const wait = has('wait', '--wait');
|
|
170
268
|
const force = has('force', '--force', '-f');
|
|
171
|
-
const plan = await buildUpdatePlan({ home, force: true });
|
|
172
|
-
if (!json)
|
|
269
|
+
const plan = await buildUpdatePlan({ home, force: true, channel });
|
|
270
|
+
if (!json) {
|
|
173
271
|
out(renderUpdatePlan(plan));
|
|
272
|
+
if (plan.channelKind === 'git-ref')
|
|
273
|
+
printDevNotice();
|
|
274
|
+
}
|
|
174
275
|
if (!plan.ok && plan.blockers.length > 0) {
|
|
175
276
|
if (json)
|
|
176
277
|
out(JSON.stringify({ stage: 'blocked', reason: plan.blockers.join('; ') }, null, 2));
|
|
@@ -178,6 +279,17 @@ export async function runUpdateCommand(args = []) {
|
|
|
178
279
|
return 1;
|
|
179
280
|
}
|
|
180
281
|
if (!plan.targetVersion) {
|
|
282
|
+
/**
|
|
283
|
+
* §12.5: 源不可达 / 版本不存在 → **拒绝并说清**, 退出码 2。
|
|
284
|
+
* 绝不打印"✅ 已是最新, 无需更新" (那正是这次要收敛掉的骗人行为)。
|
|
285
|
+
*/
|
|
286
|
+
if (REFUSED_STATUSES.includes(plan.check.status)) {
|
|
287
|
+
if (json)
|
|
288
|
+
out(JSON.stringify({ stage: 'blocked', refused: true, status: plan.check.status, reason: plan.check.reason }, null, 2));
|
|
289
|
+
err(`${RED}✗ 拒绝更新${RESET} (${plan.check.status}): ${plan.check.reason || '源不可达 / 版本不存在'}`);
|
|
290
|
+
err(' 没有安装任何东西, 也没有静默退回旧版。');
|
|
291
|
+
return 2;
|
|
292
|
+
}
|
|
181
293
|
if (json)
|
|
182
294
|
out(JSON.stringify({ stage: 'succeeded', ok: true, reason: '已是最新', from: plan.currentVersion, to: plan.currentVersion }, null, 2));
|
|
183
295
|
out(`${GREEN}✓ 已是最新, 无需更新${RESET} (${plan.currentVersion})`);
|
|
@@ -185,27 +297,34 @@ export async function runUpdateCommand(args = []) {
|
|
|
185
297
|
}
|
|
186
298
|
const res = await applyUpdate({
|
|
187
299
|
home,
|
|
300
|
+
channel,
|
|
188
301
|
strategy: wait ? 'wait' : 'now',
|
|
189
302
|
force,
|
|
190
303
|
onStage: json ? undefined : (stage, detail) => out(` [${stage}] ${detail}`),
|
|
191
304
|
});
|
|
192
305
|
if (json) {
|
|
193
306
|
out(JSON.stringify(res, null, 2));
|
|
194
|
-
return res.ok ? 0 : 1;
|
|
307
|
+
return res.ok ? 0 : (res.stage === 'blocked' ? 2 : 1);
|
|
195
308
|
}
|
|
196
309
|
if (res.ok) {
|
|
197
310
|
out(`${GREEN}✓ 更新成功${RESET}: ${res.from} → ${res.to} (${(res.durationMs / 1000).toFixed(1)}s)`);
|
|
311
|
+
if (isDevIdentity(res.to)) {
|
|
312
|
+
out(` dev 快照已记录: commit ${res.to.split('+dev.')[1] || '未知'} (ref refs/heads/master) — bolloon update status 可查`);
|
|
313
|
+
}
|
|
198
314
|
out(' 正在做更新后健康检查...');
|
|
199
315
|
const health = await runDoctor({ bolloonHome: home }).catch(() => null);
|
|
200
316
|
if (health) {
|
|
201
317
|
out(renderDoctor(health));
|
|
202
318
|
}
|
|
319
|
+
if (isDevIdentity(res.to))
|
|
320
|
+
printDevNotice();
|
|
203
321
|
out(`${YELLOW}请重启 bolloon 以使用新版本${RESET}`);
|
|
204
322
|
return 0;
|
|
205
323
|
}
|
|
206
324
|
if (res.stage === 'blocked') {
|
|
325
|
+
const refused = /拒绝更新|github_unavailable|cross_check_mismatch/.test(res.reason || '');
|
|
207
326
|
err(`${YELLOW}! ${res.reason}${RESET}`);
|
|
208
|
-
return 1;
|
|
327
|
+
return refused ? 2 : 1;
|
|
209
328
|
}
|
|
210
329
|
err(`${RED}✗ 更新失败${RESET} (${res.stage}): ${res.reason}`);
|
|
211
330
|
err(` 旧版本仍在: ${res.from} — 可继续使用; 详情: bolloon update status`);
|
|
@@ -214,21 +333,17 @@ export async function runUpdateCommand(args = []) {
|
|
|
214
333
|
return 1;
|
|
215
334
|
}
|
|
216
335
|
// ── 默认: 只检查, 给清晰结论 (显式命令 = 忽略节流) ──
|
|
217
|
-
const r = await checkForUpdate({ home, force: true });
|
|
336
|
+
const r = await checkForUpdate({ home, force: true, channel });
|
|
218
337
|
if (json) {
|
|
219
338
|
out(JSON.stringify(r, null, 2));
|
|
220
|
-
return r.status
|
|
339
|
+
return checkExitCode(r.status);
|
|
221
340
|
}
|
|
222
341
|
out(renderCheckResult(r));
|
|
223
342
|
if (r.status !== 'up_to_date' && r.status !== 'check_skipped') {
|
|
224
343
|
out('');
|
|
225
344
|
out(`${YELLOW}提示${RESET}: 本次只检查, 没有安装任何东西。`);
|
|
226
345
|
}
|
|
227
|
-
|
|
228
|
-
return 2;
|
|
229
|
-
if (r.status === 'update_available' && compareVersions(r.currentVersion, r.latestVersion || r.currentVersion) >= 0)
|
|
230
|
-
return 0;
|
|
231
|
-
return 0;
|
|
346
|
+
return checkExitCode(r.status);
|
|
232
347
|
}
|
|
233
348
|
// ── doctor ──────────────────────────────────────────────────────────────────
|
|
234
349
|
export async function runDoctorCommand(args = []) {
|
|
@@ -323,12 +438,12 @@ export async function runRuntimeCommand(args = []) {
|
|
|
323
438
|
}
|
|
324
439
|
export const UPDATE_HELP = `
|
|
325
440
|
${BOLD}版本与更新:${RESET} (子命令一律裸词, 不带 -- 前缀)
|
|
326
|
-
bolloon --version 版本 +
|
|
441
|
+
bolloon --version 版本 + 安装方式/目录/入口/通道/来源身份/上游/是否最新
|
|
327
442
|
bolloon --version verbose 诊断版 (构建时间/git/Node/npm/Python/配置目录/最近更新)
|
|
328
443
|
bolloon --version json 机器版 (同一份 VersionInfo, 给脚本与问题报告)
|
|
329
444
|
bolloon update 只检查并给结论 (不安装任何东西)
|
|
330
445
|
bolloon update plan 更新计划: 要更新什么 / 不会动什么 / 风险检查
|
|
331
|
-
bolloon update status 更新状态:
|
|
446
|
+
bolloon update status 更新状态: 装的哪个源+版本/commit+能切回哪/最近检查/失败/开关
|
|
332
447
|
bolloon update history [N] 最近 N 次更新 (时间 · 版本变化 · 结果 · 耗时 · 原因)
|
|
333
448
|
bolloon update now 真正执行更新 (默认先看计划)
|
|
334
449
|
bolloon update now wait 等当前 Run 结束后再更新 (有长期任务时的默认建议)
|
|
@@ -336,6 +451,15 @@ ${BOLD}版本与更新:${RESET} (子命令一律裸词, 不带 -- 前缀)
|
|
|
336
451
|
bolloon doctor json 同上, 机器可读
|
|
337
452
|
bolloon runtime [plan|install|json] 运行时 (Node/npm/Git/Python) 检查与安装 (install 需显式 yes)
|
|
338
453
|
|
|
454
|
+
${BOLD}双源 (--channel):${RESET}
|
|
455
|
+
--channel stable (默认) npm registry 是权威 (dist-tags.latest), GitHub Tag/Release 只做交叉校验;
|
|
456
|
+
比较语义 = semver (npm 的 latest ↔ GitHub 同名 tag)
|
|
457
|
+
--channel dev GitHub master HEAD 是唯一源; 比较语义 = git ref + commit sha (不用 semver);
|
|
458
|
+
身份 = <版本>+dev.<sha7>; 未走发布门, 会打印警告并记录 sha
|
|
459
|
+
一键回稳定版: bolloon update now --channel stable
|
|
460
|
+
--channel 只覆盖本次进程, 不落盘 (config 里的 updateChannel 仍是默认值)
|
|
461
|
+
源不可达 / 版本不存在 → 拒绝并说清 (退出码 2), 不会静默装回旧版
|
|
462
|
+
|
|
339
463
|
${BOLD}开关 (config.json):${RESET}
|
|
340
464
|
checkUpdates (默认 true) 启动时后台检查
|
|
341
465
|
autoInstall (默认 false) 自动安装 —— 默认关, 需显式打开
|
|
@@ -344,6 +468,8 @@ ${BOLD}环境变量 (临时覆盖, 不落盘):${RESET}
|
|
|
344
468
|
BOLLOON_SKIP_UPDATE=1 本次不检查
|
|
345
469
|
BOLLOON_AUTO_UPDATE=1 本次允许自动安装
|
|
346
470
|
BOLLOON_UPDATE_CHANNEL=stable|beta|dev
|
|
471
|
+
BOLLOON_GITHUB_API 覆盖 GitHub API 根 (镜像/受控验收用)
|
|
472
|
+
BOLLOON_DEV_SOURCE_DIR dev 快照用本地源码树 (必须与该 sha 一致, 否则拒绝)
|
|
347
473
|
|
|
348
|
-
${BOLD}退出码:${RESET} 0 正常 · 1 执行失败 · 2 检查不可用 (离线/registry
|
|
474
|
+
${BOLD}退出码:${RESET} 0 正常 · 1 执行失败 · 2 检查不可用 (离线/registry 不可用/GitHub 不可用/两源不一致/读不到本地版本)
|
|
349
475
|
`;
|
package/dist/cli-entry.js
CHANGED
|
@@ -22,15 +22,19 @@ import { discoverEngines, delegateToEngine } from './external-engines/index.js';
|
|
|
22
22
|
import { x402CheckBalance, x402Fetch } from './agents/x402/x402Pay.js';
|
|
23
23
|
import { runVersionCommand, runUpdateCommand, runDoctorCommand, runRuntimeCommand, UPDATE_HELP } from './cli/update-commands.js';
|
|
24
24
|
import { collectVersionInfo } from './utils/version-info.js';
|
|
25
|
+
// 2026-09-27: 启动前言的默认静默 + 显式查询命令 (与 `src/index.ts` 同一套判断)
|
|
26
|
+
import { startupPreambleVisible } from './cli/startup-notice.js';
|
|
27
|
+
import { logStartupLine } from './cli/log-gate.js';
|
|
25
28
|
// 2026-09-21 (P3): 统一 JSON 信封 + 命令组 (network/agent/task/wallet/payment/trade)
|
|
26
29
|
import { runServiceGroup, GROUPS_HELP } from './cli/commands/index.js';
|
|
27
30
|
// 2026-09-21 (P4): MCP 适配层 (`bolloon mcp serve` = stdio, 只调 P3 服务层)
|
|
28
31
|
import { runMcpCommand } from './cli/commands/mcp.js';
|
|
29
|
-
import { legacyJson } from './cli/protocol-envelope.js';
|
|
32
|
+
import { legacyJson, parseFlags, runCommand } from './cli/protocol-envelope.js';
|
|
33
|
+
import { identityCommand } from './cli/identity-command.js';
|
|
30
34
|
import { createRequire } from 'module';
|
|
31
35
|
const _require = createRequire(import.meta.url);
|
|
32
36
|
/** P3 `bolloon task <子命令>` 的已知子命令 —— 其它一律当 M1 任务正文 (既有体验不动) */
|
|
33
|
-
const TASK_SUBCOMMANDS = new Set(['send', 'list', 'status', 'cancel', 'retry', 'result', 'inbox', 'accept', 'reject', 'run', 'complete', 'publish', 'board', 'claim']);
|
|
37
|
+
const TASK_SUBCOMMANDS = new Set(['send', 'list', 'status', 'cancel', 'retry', 'result', 'inbox', 'accept', 'reject', 'run', 'complete', 'publish', 'board', 'claim', 'announce', 'trail', 'post', 'group']);
|
|
34
38
|
const isWindows = process.platform === 'win32';
|
|
35
39
|
// ANSI 颜色 — Bolloon Web UI 配色 truecolor
|
|
36
40
|
function _fg(r, g, b) { return `\x1b[38;2;${r};${g};${b}m`; }
|
|
@@ -62,12 +66,17 @@ ${BOLD}选项:${RESET}
|
|
|
62
66
|
|
|
63
67
|
${BOLD}命令:${RESET}
|
|
64
68
|
bolloon setup 初始化向导 (你的称呼 + 模型供应商 + API key + 连通性测试)
|
|
69
|
+
bolloon identity init 非交互建本机身份 (~/.bolloon/identity.json, 0600, 幂等; 新机器/第二实例用)
|
|
70
|
+
bolloon identity show 看本机身份 (只出 DID/指纹, 绝不打印私钥)
|
|
65
71
|
bolloon update [plan|status|history|now] 检查更新 / 计划 / 状态 / 历史 / 执行 (默认只检查)
|
|
66
72
|
bolloon doctor 安装入口 + 版本事实 + 更新状态自洽性诊断
|
|
67
73
|
bolloon runtime [plan|install] 运行时 (Node/npm/Git/Python) 检查与安装
|
|
68
74
|
bolloon model [name] [model] 列出 / 切换模型供应商 (如: bolloon model deepseek deepseek-v4-flash)
|
|
69
75
|
bolloon model key <name> 配置某供应商的 API key (隐藏输入, 不回显)
|
|
70
76
|
bolloon model test [name] 测试供应商连通性
|
|
77
|
+
bolloon model list [name] 看模型发现目录 (P5)
|
|
78
|
+
bolloon model refresh [name] 重取上游模型目录 (--clear 清缓存)
|
|
79
|
+
bolloon model admit <name> <model> 手输一个目录里没有的模型名
|
|
71
80
|
bolloon read <file> 读取文档
|
|
72
81
|
bolloon summarize <file> 总结文档
|
|
73
82
|
bolloon improve <file> <req> 改进文档
|
|
@@ -203,6 +212,9 @@ function parseArgs() {
|
|
|
203
212
|
case 'setup':
|
|
204
213
|
case 'init':
|
|
205
214
|
return { mode: 'setup', args: args.slice(1) };
|
|
215
|
+
// 2026-09-24: 非交互建本机身份 (`bolloon identity init` → ~/.bolloon/identity.json)
|
|
216
|
+
case 'identity':
|
|
217
|
+
return { mode: 'identity', args: args.slice(1) };
|
|
206
218
|
case 'read':
|
|
207
219
|
case 'summarize':
|
|
208
220
|
case 'improve':
|
|
@@ -562,36 +574,121 @@ async function handleP2pCommand(p2pArgs) {
|
|
|
562
574
|
// 那是常驻模块, 句柄不会自己关 → 不显式退出会"输出完了还挂着"。
|
|
563
575
|
process.exit(info.ok ? 0 : 1);
|
|
564
576
|
}
|
|
577
|
+
/** 真终端判定: stdin 与 stdout 都是 TTY 才算交互 (管道 / 重定向 = 脚本可读输出) */
|
|
578
|
+
function isRealTty() {
|
|
579
|
+
return !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* `bolloon model [...]` — 模型供应商/模型切换的唯一命令行入口。
|
|
583
|
+
*
|
|
584
|
+
* leo 2026-09-27 口径: **`bolloon model` 就是"切换"的启动命令** —— 真终端里敲完直接就是选择界面
|
|
585
|
+
* (与 `bolloon model pick` 同一实现, 只是用户不必知道 pick 这个词), 第一屏就是带序号/状态/当前项
|
|
586
|
+
* 标记的供应商列表, 选完往下走; **不许**先刷一坨供应商清单+用法再问。
|
|
587
|
+
* 管道/非 TTY 才退回清单+用法 (脚本可读, 不会卡在等待输入)。
|
|
588
|
+
* `pick` / `list` 两个子命令都保留 (pick 是显式写法, list 只读列表)。
|
|
589
|
+
*/
|
|
565
590
|
async function handleModelCommand(modelArgs) {
|
|
566
|
-
const { llmConfigStore
|
|
591
|
+
const { llmConfigStore } = await import('./llm/config-store.js');
|
|
592
|
+
const { effectiveModelConfig, formatEffectiveModel } = await import('./llm/model-selection.js');
|
|
567
593
|
await llmConfigStore.initialize();
|
|
568
|
-
|
|
594
|
+
const tty = isRealTty();
|
|
595
|
+
// ── 真终端 + 裸敲: 直接进选择器 (第一屏 = 供应商列表) ──────────
|
|
596
|
+
if (modelArgs.length === 0 && tty) {
|
|
597
|
+
const { runModelCommand, modelTtyIO, askHiddenLineEof, askLineEof } = await import('./cli/setup-wizard.js');
|
|
598
|
+
const out = await runModelCommand('pick', await useTui() ? modelTtyIO() : {
|
|
599
|
+
live: true, // 每一步当场打到终端 (不是等整轮结束才一次性回显)
|
|
600
|
+
print: (l) => process.stdout.write(l + '\n'),
|
|
601
|
+
askHidden: async (q) => { const r = await askHiddenLineEof(q); return r.eof ? null : r.value; },
|
|
602
|
+
ask: async (q, opts) => { const r = await askLineEof(q, opts); return r.eof ? null : r.value; },
|
|
603
|
+
});
|
|
604
|
+
for (const line of String(out).split('\n'))
|
|
605
|
+
if (line !== '')
|
|
606
|
+
console.log(line);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
// ── 无参 + 非终端 (管道/脚本): 列出**全部**供应商 (分组带家数) + 当前**真实生效**的那一份 + 用法 ──
|
|
569
610
|
if (modelArgs.length === 0) {
|
|
570
|
-
const
|
|
571
|
-
|
|
611
|
+
const eff = await effectiveModelConfig({}).catch(() => null);
|
|
612
|
+
const mc = await import('./llm/model-catalog.js');
|
|
613
|
+
const summaries = await mc.buildProviderSummaries({});
|
|
614
|
+
const pc = await import('./llm/provider-catalog.js');
|
|
615
|
+
await pc.initializeProviderCatalog();
|
|
616
|
+
console.log(`\n${BOLD}模型供应商${RESET} (当前生效: ${eff ? `${eff.provider}/${eff.model}` : '读不出来'})\n`);
|
|
617
|
+
if (eff)
|
|
618
|
+
console.log(` ${formatEffectiveModel(eff)}`);
|
|
572
619
|
console.log('─'.repeat(58));
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
620
|
+
// 非终端里没有"固定高度视窗 + 折叠"那套交互 (那是全屏 TUI 的事), 所以这里**整份列全**:
|
|
621
|
+
// 内置 13 + 自定义 + 目录全部 逐行出, 分组标题带家数。只给"分组计数"是这条线要修的病 ——
|
|
622
|
+
// 脚本要能 grep 到任何一家 (含"需专用鉴权 (未支持)""无基址"的那些)。
|
|
623
|
+
const ordered = mc.orderProvidersForMenu(summaries);
|
|
624
|
+
let lastGroup = '';
|
|
625
|
+
for (const s of ordered) {
|
|
626
|
+
const tier = mc.providerTierOf(s);
|
|
627
|
+
const g = mc.PROVIDER_GROUPS[tier];
|
|
628
|
+
if (g !== lastGroup) {
|
|
629
|
+
lastGroup = g;
|
|
630
|
+
const n = ordered.filter((x) => mc.providerTierOf(x) === tier).length;
|
|
631
|
+
console.log(` ── ${g} (${n} 家)${mc.providerTierCollapsedByDefault(tier) ? ' · 交互界面里默认收起, 空格/→ 展开' : ''}`);
|
|
632
|
+
}
|
|
633
|
+
console.log(` ${mc.formatProviderMenuRow(s)}`);
|
|
579
634
|
}
|
|
635
|
+
console.log(` ${mc.providerGroupSummary(summaries)}`);
|
|
636
|
+
console.log(` ${pc.catalogStatusLine()}`);
|
|
637
|
+
console.log(` ${pc.catalogGroupLine()}`);
|
|
580
638
|
console.log(`\n${BOLD}用法:${RESET}`);
|
|
639
|
+
console.log(` bolloon model # 终端里直接进选择界面 (固定高度视窗 + 分组折叠; 上面这份全量列表是管道/脚本的可读输出)`);
|
|
640
|
+
console.log(` bolloon model pick # 同上, 显式写法 (供应商→凭证→模型→参数→作用域→测试→确认)`);
|
|
581
641
|
console.log(` bolloon model <name> # 切换到该供应商`);
|
|
582
642
|
console.log(` bolloon model <name> <model> # 切换并指定模型`);
|
|
643
|
+
console.log(` bolloon model list [name] # 看模型发现目录 (只读)`);
|
|
644
|
+
console.log(` bolloon model refresh [name] # 重取上游目录 (--clear 清缓存)`);
|
|
645
|
+
console.log(` bolloon model admit <name> <m> # 手输一个目录里没有的模型名`);
|
|
646
|
+
console.log(` bolloon model catalog # 供应商目录: 是几号的/新鲜度/族分布 (只读)`);
|
|
647
|
+
console.log(` bolloon model catalog list [词] # 逐家看 (**默认全量**, 含"需专用鉴权/无基址"并标原因; --usable 只看能用的)`);
|
|
648
|
+
console.log(` bolloon model catalog refresh # 运行期真拉最新目录 (落盘 0600, 记来源与时间)`);
|
|
583
649
|
console.log(` 示例: bolloon model deepseek deepseek-v4-flash`);
|
|
584
650
|
return;
|
|
585
651
|
}
|
|
586
|
-
// 有参: 统一交给 setup-wizard 的 runModelCommand
|
|
587
|
-
// (切换 / <provider> <model> / key <provider> / test / status 一套语义,
|
|
588
|
-
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
652
|
+
// ── 有参: 统一交给 setup-wizard 的 runModelCommand ───────────
|
|
653
|
+
// (pick 分步选择 / 切换 / <provider> <model> / key <provider> / test / status 一套语义,
|
|
654
|
+
// 与 CLI 会话内 /model 完全一致 —— 写配置与重建运行时的逻辑只有 selectModel 一处)
|
|
655
|
+
const { runModelCommand, modelTtyIO, askHiddenLineEof, askLineEof } = await import('./cli/setup-wizard.js');
|
|
656
|
+
// `--verbose` 交给界面层决定"打不打内部细节", 不进参数解析 (解析器不认识这个词会被当值)
|
|
657
|
+
const wantVerbose = modelArgs.includes('--verbose');
|
|
658
|
+
const args = modelArgs.filter((a) => a !== '--verbose');
|
|
659
|
+
const out = await runModelCommand(args.join(' '), (await useTui()) && tty ? {
|
|
660
|
+
...modelTtyIO(),
|
|
661
|
+
...(wantVerbose ? { verbose: true } : {}),
|
|
662
|
+
} : {
|
|
663
|
+
// 非 TTY (管道/脚本) / 显式关掉 TUI: 逐行问答 —— 输出可读、不卡死、行为与以前一致
|
|
664
|
+
live: tty,
|
|
665
|
+
print: (l) => process.stdout.write(l + '\n'),
|
|
666
|
+
askHidden: async (q) => { const r = await askHiddenLineEof(q); return r.eof ? null : r.value; },
|
|
667
|
+
ask: async (q, opts) => { const r = await askLineEof(q, opts); return r.eof ? null : r.value; },
|
|
668
|
+
...(wantVerbose ? { verbose: true } : {}),
|
|
592
669
|
});
|
|
593
670
|
for (const line of String(out).split('\n'))
|
|
594
|
-
|
|
671
|
+
if (line !== '')
|
|
672
|
+
console.log(line);
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* 这个环境要不要用全屏 TUI 选择器。
|
|
676
|
+
* 三判据: ① 用户没显式关 (`BOLLOON_NO_TUI=1`); ② 真 TTY; ③ `tuiCapable()` (终端够高/能拿 raw mode)。
|
|
677
|
+
* 任何一条不满足 → 退回逐行问答 (**不静默变样子**: 管道输出仍是可读清单+用法)。
|
|
678
|
+
*/
|
|
679
|
+
function useTui() {
|
|
680
|
+
return (async () => {
|
|
681
|
+
if (process.env.BOLLOON_NO_TUI)
|
|
682
|
+
return false;
|
|
683
|
+
try {
|
|
684
|
+
// 同步判据在 tui-select 里 (它不许在非 TTY 里抢输入)
|
|
685
|
+
const { tuiCapable } = await import('./cli/tui-select.js');
|
|
686
|
+
return !!tuiCapable();
|
|
687
|
+
}
|
|
688
|
+
catch {
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
})();
|
|
595
692
|
}
|
|
596
693
|
/** `bolloon setup` — 首次运行初始化向导 (用户身份 + 模型供应商 + API key + 连通性测试) */
|
|
597
694
|
async function handleSetupCommand(setupArgs) {
|
|
@@ -609,6 +706,7 @@ async function handleSetupCommand(setupArgs) {
|
|
|
609
706
|
console.log(`${BOLD}bolloon setup${RESET} — 初始化 Bolloon (用户身份 + 模型供应商 + API key)`);
|
|
610
707
|
console.log('');
|
|
611
708
|
console.log(' 无参数 交互式向导 (推荐)');
|
|
709
|
+
console.log(' status 只读: 看初始化状态/门禁/就绪度全量 (启动面板那一行"详情"指的就是它)');
|
|
612
710
|
console.log(' --provider <名> 指定供应商 (deepseek / minimax / openai / anthropic / ...)');
|
|
613
711
|
console.log(' --api-key <key> 直接给 key (脚本用; 交互模式会隐藏输入)');
|
|
614
712
|
console.log(' --model <名> 指定模型');
|
|
@@ -617,6 +715,15 @@ async function handleSetupCommand(setupArgs) {
|
|
|
617
715
|
console.log(' repair-runtime 检查并补装缺失运行时 (Node/npm/Git/Python)');
|
|
618
716
|
return;
|
|
619
717
|
}
|
|
718
|
+
// 2026-09-27: `bolloon setup status` —— **启动前言搬去的地方**。
|
|
719
|
+
// 启动时那一整段 (初始化状态/就绪度/已完成/配置来源/已存输入/缺什么/下一步) 默认不上屏,
|
|
720
|
+
// 要看全量就显式敲这条 (只读, 不写任何文件; 退出码 0 = ready, 1 = 未就绪 —— 脚本可用)。
|
|
721
|
+
if (setupArgs[0] === 'status' || setupArgs.includes('--status')) {
|
|
722
|
+
const { evaluateSetup, describeSetup } = await import('./setup/setup-store.js');
|
|
723
|
+
const ev = await evaluateSetup();
|
|
724
|
+
process.stdout.write(describeSetup(ev) + '\n');
|
|
725
|
+
process.exit(ev.gate === 'ready' ? 0 : 1);
|
|
726
|
+
}
|
|
620
727
|
const apiKey = val('--api-key');
|
|
621
728
|
const needsInteractive = !apiKey;
|
|
622
729
|
const r = await runSetupWizard({
|
|
@@ -795,7 +902,13 @@ async function startWebServer(additionalArgs) {
|
|
|
795
902
|
}
|
|
796
903
|
// 启动 CLI
|
|
797
904
|
async function startCLI(additionalArgs) {
|
|
798
|
-
|
|
905
|
+
// 2026-09-27 (leo 口径: 打完 `bolloon --cli` 直接看到面板): 这句"启动命令行界面..."属于**启动前言**,
|
|
906
|
+
// 默认不上屏 (照落启动日志); `--verbose` / `BOLLOON_VERBOSE=1` / `BOLLOON_STARTUP_PREAMBLE=1` 逐字回来。
|
|
907
|
+
// (gui/web 那两个面的 banner 不动 —— 它们没有"面板", 输出就是交付物。)
|
|
908
|
+
if (startupPreambleVisible())
|
|
909
|
+
log('启动命令行界面...', CYAN);
|
|
910
|
+
else
|
|
911
|
+
logStartupLine('启动命令行界面...');
|
|
799
912
|
try {
|
|
800
913
|
const mainPath = getMainScript();
|
|
801
914
|
const cliArgs = [...additionalArgs];
|
|
@@ -890,6 +1003,10 @@ async function main() {
|
|
|
890
1003
|
case 'setup':
|
|
891
1004
|
await handleSetupCommand(args);
|
|
892
1005
|
break;
|
|
1006
|
+
// 2026-09-24: bolloon identity — 非交互建/看本机身份 (新机器/第二实例用; 幂等, 不打印私钥)
|
|
1007
|
+
case 'identity':
|
|
1008
|
+
process.exit(await runCommand(parseFlags(args), identityCommand));
|
|
1009
|
+
break;
|
|
893
1010
|
case 'passthrough':
|
|
894
1011
|
// 传递所有参数给主程序
|
|
895
1012
|
try {
|
|
@@ -56,6 +56,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
56
56
|
exports.maskPhone = exports.maskEmail = exports.ContactChain = void 0;
|
|
57
57
|
exports.contactChain = contactChain;
|
|
58
58
|
const goal_store_js_1 = require("../goal-store.js");
|
|
59
|
+
// 2026-09-25 (M0 接线冻结, 规则 ②): Goal 状态变更只有一个漏斗
|
|
60
|
+
const goal_state_reducer_js_1 = require("../goal-state-reducer.js");
|
|
59
61
|
const run_store_js_1 = require("../run-store.js");
|
|
60
62
|
const external_events_js_1 = require("../external-events.js");
|
|
61
63
|
const store_js_1 = require("./store.js");
|
|
@@ -360,7 +362,14 @@ class ContactChain {
|
|
|
360
362
|
note: `等 ${contact.displayValue} 回复 (通道 ${contact.provider})`,
|
|
361
363
|
};
|
|
362
364
|
await (0, external_events_js_1.bindExternalWait)(opts.goalId, wait);
|
|
363
|
-
|
|
365
|
+
// 规则 ②: 进入"等外部"也是一种 Goal 状态变更 → 走 Goal reducer
|
|
366
|
+
await (0, goal_state_reducer_js_1.reduceGoalState)({
|
|
367
|
+
goalId: opts.goalId,
|
|
368
|
+
intent: 'external_wait_enter',
|
|
369
|
+
now: new Date(this.now()).toISOString(),
|
|
370
|
+
by: 'contacts',
|
|
371
|
+
externalWait: `等 ${contact.displayValue} 回复 (通道 ${contact.provider})`,
|
|
372
|
+
});
|
|
364
373
|
const g = await (0, goal_store_js_1.readGoal)(opts.goalId);
|
|
365
374
|
await this.ledger({
|
|
366
375
|
activity: 'contact.authorization_requested', contactId: contact.contactId, goalId: opts.goalId, runId: opts.runId,
|
|
@@ -492,9 +501,14 @@ class ContactChain {
|
|
|
492
501
|
await (0, external_events_js_1.clearExternalWait)(s.goalId);
|
|
493
502
|
const g = await (0, goal_store_js_1.readGoal)(s.goalId);
|
|
494
503
|
await (0, goal_store_js_1.setUnresolved)(s.goalId, Array.from(new Set([...(g?.unresolvedItems || []), `联系方式授权 ${grantId} 已撤销, 该联系不可自动重试 (转人工)`])));
|
|
495
|
-
// 撤等待 + 转人工: 不再自动唤醒, 由人接手 (
|
|
496
|
-
await (0,
|
|
497
|
-
|
|
504
|
+
// 撤等待 + 转人工: 不再自动唤醒, 由人接手 (规则 ②: 状态经 Goal reducer 写, 单一漏斗)
|
|
505
|
+
await (0, goal_state_reducer_js_1.reduceGoalState)({
|
|
506
|
+
goalId: s.goalId,
|
|
507
|
+
intent: 'contact_revoked_human',
|
|
508
|
+
now: new Date().toISOString(),
|
|
509
|
+
by: 'contacts',
|
|
510
|
+
revokeNote: `联系方式授权 ${grantId} 已撤销, 该联系不可自动重试`,
|
|
511
|
+
}).catch(() => null);
|
|
498
512
|
affected.push(s.goalId);
|
|
499
513
|
}
|
|
500
514
|
return { ok: true, affectedGoals: affected };
|
|
@@ -594,8 +608,14 @@ class ContactChain {
|
|
|
594
608
|
await (0, external_events_js_1.clearExternalWait)(s.goalId);
|
|
595
609
|
const g = await (0, goal_store_js_1.readGoal)(s.goalId);
|
|
596
610
|
await (0, goal_store_js_1.setUnresolved)(s.goalId, Array.from(new Set([...(g?.unresolvedItems || []), `联系人 ${c.displayValue} 已撤销授权, 该联系不可重试`])));
|
|
597
|
-
|
|
598
|
-
await (0,
|
|
611
|
+
// 规则 ②: 经 Goal reducer 写 (单一漏斗)
|
|
612
|
+
await (0, goal_state_reducer_js_1.reduceGoalState)({
|
|
613
|
+
goalId: s.goalId,
|
|
614
|
+
intent: 'contact_revoked_human',
|
|
615
|
+
now: new Date().toISOString(),
|
|
616
|
+
by: 'contacts',
|
|
617
|
+
revokeNote: `联系人 ${c.displayValue} 已撤销授权, 该联系不可重试`,
|
|
618
|
+
}).catch(() => null);
|
|
599
619
|
affected.push(s.goalId);
|
|
600
620
|
}
|
|
601
621
|
return { ok: true, evidenceRef: ev.evidenceRef, affectedGoals: affected };
|