@konglx/rotom 2.21.0
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/README.md +417 -0
- package/bin/mesh-master.sh +439 -0
- package/bin/rotom +29 -0
- package/bin/rotom-link.sh +136 -0
- package/bin/rotom-send-with-status +57 -0
- package/bin/rotom-up.sh +428 -0
- package/dist/cli/ask.js +62 -0
- package/dist/cli/common.js +321 -0
- package/dist/cli/config.js +65 -0
- package/dist/cli/directory.js +17 -0
- package/dist/cli/executor.js +58 -0
- package/dist/cli/fed.js +91 -0
- package/dist/cli/group.js +273 -0
- package/dist/cli/identity.js +62 -0
- package/dist/cli/init.js +268 -0
- package/dist/cli/issue.js +202 -0
- package/dist/cli/join.js +170 -0
- package/dist/cli/link.js +47 -0
- package/dist/cli/master.js +51 -0
- package/dist/cli/memory.js +307 -0
- package/dist/cli/note.js +68 -0
- package/dist/cli/repo.js +77 -0
- package/dist/cli/rotom.js +277 -0
- package/dist/cli/routes.js +118 -0
- package/dist/cli/run.js +45 -0
- package/dist/cli/schedule.js +237 -0
- package/dist/cli/skill.js +173 -0
- package/dist/cli/team.js +106 -0
- package/dist/executor/claude-code-hook.cjs +80 -0
- package/dist/executor/cli-executor.js +8 -0
- package/dist/executor/executors/claude-code.js +780 -0
- package/dist/executor/executors/codex.js +719 -0
- package/dist/executor/executors/hermes-cli.js +855 -0
- package/dist/executor/executors/openclaw.js +467 -0
- package/dist/executor/executors/pi.js +514 -0
- package/dist/executor/index.js +269 -0
- package/dist/executor/jsonrpc-transport.js +125 -0
- package/dist/executor/process-runner.js +101 -0
- package/dist/executor/reasoning-status.js +83 -0
- package/dist/executor/repo-cache.js +502 -0
- package/dist/executor/session-store.js +188 -0
- package/dist/executor/worker-chat.js +257 -0
- package/dist/executor/worker-connection.js +89 -0
- package/dist/executor/worker-issue.js +264 -0
- package/dist/executor/worker.js +877 -0
- package/dist/link/pending-requests.js +72 -0
- package/dist/link/server.js +233 -0
- package/dist/link/visibility-store.js +58 -0
- package/dist/master/api/agents.js +333 -0
- package/dist/master/api/artifacts.js +271 -0
- package/dist/master/api/domains.js +64 -0
- package/dist/master/api/groups.js +635 -0
- package/dist/master/api/guidance-templates.js +147 -0
- package/dist/master/api/index.js +89 -0
- package/dist/master/api/issues-patrol.js +172 -0
- package/dist/master/api/issues.js +663 -0
- package/dist/master/api/links-patrol.js +168 -0
- package/dist/master/api/links.js +114 -0
- package/dist/master/api/memory.js +259 -0
- package/dist/master/api/messages.js +157 -0
- package/dist/master/api/notes.js +77 -0
- package/dist/master/api/schedule-patterns.js +133 -0
- package/dist/master/api/schedules.js +272 -0
- package/dist/master/api/sessions.js +158 -0
- package/dist/master/api/share.js +269 -0
- package/dist/master/api/skills.js +190 -0
- package/dist/master/api/teams.js +122 -0
- package/dist/master/api/uploads.js +245 -0
- package/dist/master/auth.js +134 -0
- package/dist/master/dashboard/animations/calico-dozing.apng +0 -0
- package/dist/master/dashboard/animations/calico-error.apng +0 -0
- package/dist/master/dashboard/animations/calico-happy.apng +0 -0
- package/dist/master/dashboard/animations/calico-notification.apng +0 -0
- package/dist/master/dashboard/animations/calico-sleeping.apng +0 -0
- package/dist/master/dashboard/animations/calico-thinking.apng +0 -0
- package/dist/master/dashboard/animations/calico-waking.apng +0 -0
- package/dist/master/dashboard/assets/ApprovalCard-C38VV6ko.css +1 -0
- package/dist/master/dashboard/assets/ApprovalCard-CHPh2dmE.js +17 -0
- package/dist/master/dashboard/assets/ArtifactPanel-P_2gAP7v.js +1 -0
- package/dist/master/dashboard/assets/ArtifactPanel-aGHySny5.css +1 -0
- package/dist/master/dashboard/assets/css.worker-DaIe3gwK.js +84 -0
- package/dist/master/dashboard/assets/editor.worker-BCzxt1at.js +12 -0
- package/dist/master/dashboard/assets/html.worker-CKrFyw_2.js +461 -0
- package/dist/master/dashboard/assets/index-CChrTn81.css +32 -0
- package/dist/master/dashboard/assets/index-Dhu4SN1z.js +181 -0
- package/dist/master/dashboard/assets/json.worker-B7c_PmGb.js +49 -0
- package/dist/master/dashboard/assets/markdown-CeN5IgdF.js +29 -0
- package/dist/master/dashboard/assets/monaco-core-DyX1CsEw.css +1 -0
- package/dist/master/dashboard/assets/monaco-core-oQiQUisy.js +833 -0
- package/dist/master/dashboard/assets/monaco-setup-CiOPQdmo.js +1 -0
- package/dist/master/dashboard/assets/react-vendor-C8IxlyCR.js +67 -0
- package/dist/master/dashboard/assets/ts.worker-BhkL8olL.js +51334 -0
- package/dist/master/dashboard/assets/useMonaco-ILb4vyPh.js +12 -0
- package/dist/master/dashboard/assets/vite-preload-CxJPbCTl.js +1 -0
- package/dist/master/dashboard/debug-auth.html +197 -0
- package/dist/master/dashboard/favicon.ico +0 -0
- package/dist/master/dashboard/index.html +20 -0
- package/dist/master/dashboard/rotom-avatar.png +0 -0
- package/dist/master/db/agent-sessions.js +60 -0
- package/dist/master/db/agent-visibility.js +64 -0
- package/dist/master/db/agents.js +119 -0
- package/dist/master/db/ask-bridges.js +157 -0
- package/dist/master/db/build-update.js +59 -0
- package/dist/master/db/core.js +82 -0
- package/dist/master/db/domains.js +80 -0
- package/dist/master/db/groups.js +316 -0
- package/dist/master/db/guidance-templates.js +58 -0
- package/dist/master/db/index.js +12 -0
- package/dist/master/db/internal.js +45 -0
- package/dist/master/db/issues-patrol.js +81 -0
- package/dist/master/db/issues.js +373 -0
- package/dist/master/db/links.js +221 -0
- package/dist/master/db/master-node.js +43 -0
- package/dist/master/db/memory.js +272 -0
- package/dist/master/db/messages.js +210 -0
- package/dist/master/db/notes.js +55 -0
- package/dist/master/db/schedule-patterns.js +56 -0
- package/dist/master/db/schedules.js +135 -0
- package/dist/master/db/skills.js +144 -0
- package/dist/master/db/team.js +88 -0
- package/dist/master/db/types.js +10 -0
- package/dist/master/db.js +12 -0
- package/dist/master/embedded.js +133 -0
- package/dist/master/federation/client.js +283 -0
- package/dist/master/federation/identity.js +133 -0
- package/dist/master/federation/manager.js +267 -0
- package/dist/master/federation/publisher.js +87 -0
- package/dist/master/federation/self-publisher.js +69 -0
- package/dist/master/federation/server.js +487 -0
- package/dist/master/group-paths.js +208 -0
- package/dist/master/offline-queue.js +38 -0
- package/dist/master/opc-bootstrap.js +245 -0
- package/dist/master/patrol-terminal.js +275 -0
- package/dist/master/repo-scan.js +188 -0
- package/dist/master/router.js +214 -0
- package/dist/master/scheduler-handlers.js +510 -0
- package/dist/master/scheduler.js +201 -0
- package/dist/master/server.js +203 -0
- package/dist/master/services/link-collector.js +82 -0
- package/dist/master/services/link-patrol-bootstrap.js +50 -0
- package/dist/master/services/memory-extract-prompt.js +34 -0
- package/dist/master/services/patrol-bootstrap.js +63 -0
- package/dist/master/share-tokens.js +56 -0
- package/dist/master/terminal-hub.js +300 -0
- package/dist/master/uploads.js +108 -0
- package/dist/master/util/fs.js +100 -0
- package/dist/master/util/paths.js +50 -0
- package/dist/master/util/persona.js +10 -0
- package/dist/master/ws-hub/connection.js +928 -0
- package/dist/master/ws-hub/conversation.js +290 -0
- package/dist/master/ws-hub/directory.js +70 -0
- package/dist/master/ws-hub/dispatch-enrich.js +34 -0
- package/dist/master/ws-hub/hub.js +136 -0
- package/dist/master/ws-hub/index.js +9 -0
- package/dist/master/ws-hub/internal.js +35 -0
- package/dist/master/ws-hub/routing.js +295 -0
- package/dist/master/ws-hub/sessions.js +130 -0
- package/dist/master/ws-hub.js +11 -0
- package/dist/shared/agent-profile.js +44 -0
- package/dist/shared/constants.js +55 -0
- package/dist/shared/dedup.js +33 -0
- package/dist/shared/group-context.js +62 -0
- package/dist/shared/json-codec.js +33 -0
- package/dist/shared/logger.js +136 -0
- package/dist/shared/mention.js +22 -0
- package/dist/shared/network.js +40 -0
- package/dist/shared/parse.js +18 -0
- package/dist/shared/prompt-composer.js +171 -0
- package/dist/shared/protocol/client-messages.js +8 -0
- package/dist/shared/protocol/enums.js +6 -0
- package/dist/shared/protocol/federation.js +62 -0
- package/dist/shared/protocol/guards.js +87 -0
- package/dist/shared/protocol/server-messages.js +8 -0
- package/dist/shared/protocol/types.js +8 -0
- package/dist/shared/protocol.js +19 -0
- package/dist/shared/readonly-allowlist.js +122 -0
- package/dist/shared/rotom-cli-prompt.js +23 -0
- package/dist/shared/skill-context.js +19 -0
- package/dist/shared/skill-md.js +43 -0
- package/dist/shared/slash-commands.js +50 -0
- package/dist/shared/time.js +80 -0
- package/dist/shared/title.js +46 -0
- package/dist/shared/url-extractor.js +99 -0
- package/migrations/001-schema.sql +942 -0
- package/package.json +68 -0
- package/scripts/fix-node-pty-perms.mjs +46 -0
- package/skill/rotom-a2a-communicate/SKILL.md +257 -0
- package/skill/rotom-bus-host/SKILL.md +78 -0
- package/skill/rotom-bus-host/scripts/poll-replies.sh +148 -0
package/bin/rotom-up.sh
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Rotom 一站式启停脚本 —— 守护方式同时跑 Master(含 Dashboard)+ Executor。
|
|
4
|
+
#
|
|
5
|
+
# 用法: rotom-up <command> [options]
|
|
6
|
+
# 命令: start | stop | restart | status | logs
|
|
7
|
+
# 选项: --port/-p <port> --host <addr> --data/-d <dir> --no-build
|
|
8
|
+
#
|
|
9
|
+
# 运行时目录: ~/.rotom/{run,logs}
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
MASTER_JS="$SCRIPT_DIR/dist/master/server.js"
|
|
16
|
+
EXECUTOR_JS="$SCRIPT_DIR/dist/executor/index.js"
|
|
17
|
+
|
|
18
|
+
ROTOM_HOME="${ROTOM_HOME:-$HOME/.rotom}"
|
|
19
|
+
RUN_DIR="$ROTOM_HOME/run"
|
|
20
|
+
LOG_DIR="$ROTOM_HOME/logs"
|
|
21
|
+
MASTER_PID="$RUN_DIR/master.pid"
|
|
22
|
+
EXECUTOR_PID="$RUN_DIR/executor.pid"
|
|
23
|
+
MASTER_LOG="$LOG_DIR/master.log"
|
|
24
|
+
EXECUTOR_LOG="$LOG_DIR/executor.log"
|
|
25
|
+
EXECUTOR_CONFIG="$ROTOM_HOME/executor.config.json"
|
|
26
|
+
|
|
27
|
+
PORT="${MESH_MASTER_PORT:-28800}"
|
|
28
|
+
HOST="${MESH_MASTER_HOST:-0.0.0.0}"
|
|
29
|
+
# data dir 默认跟 ROTOM_HOME 保持一致 —— 让 ROTOM_HOME 真正起到"切换数据目录"的作用。
|
|
30
|
+
DATA="${MESH_MASTER_DATA:-$ROTOM_HOME}"
|
|
31
|
+
SKIP_BUILD=false
|
|
32
|
+
DEV_MODE=false
|
|
33
|
+
|
|
34
|
+
# ── 工具函数 ──────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
fix_path() {
|
|
37
|
+
[ -d "/opt/homebrew/bin" ] && export PATH="/opt/homebrew/bin:$PATH"
|
|
38
|
+
[ -d "/usr/local/bin" ] && export PATH="/usr/local/bin:$PATH"
|
|
39
|
+
[ -d "$HOME/.nodejs/bin" ] && export PATH="$HOME/.nodejs/bin:$PATH"
|
|
40
|
+
[ -s "$HOME/.nvm/nvm.sh" ] && { export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh"; }
|
|
41
|
+
[ -d "$HOME/.fnm" ] && { export PATH="$HOME/.fnm:$PATH"; eval "$(fnm env 2>/dev/null)" || true; }
|
|
42
|
+
[ -d "$HOME/.volta" ] && export PATH="$HOME/.volta/bin:$PATH"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
detect_pkg() {
|
|
46
|
+
command -v pnpm &>/dev/null && echo pnpm && return
|
|
47
|
+
command -v npm &>/dev/null && echo npm && return
|
|
48
|
+
command -v yarn &>/dev/null && echo yarn && return
|
|
49
|
+
echo ""
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# 是否需要重新 build(dist 缺失,或 src/packages 下有更新的源文件)
|
|
53
|
+
need_build() {
|
|
54
|
+
[ ! -f "$MASTER_JS" ] && return 0
|
|
55
|
+
[ ! -f "$EXECUTOR_JS" ] && return 0
|
|
56
|
+
local newer
|
|
57
|
+
newer=$(find "$SCRIPT_DIR/src" "$SCRIPT_DIR/packages" \
|
|
58
|
+
\( -name '*.ts' -o -name '*.tsx' -o -name '*.html' -o -name '*.css' \) \
|
|
59
|
+
-newer "$MASTER_JS" -print -quit 2>/dev/null || true)
|
|
60
|
+
[ -n "$newer" ]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
ensure_built() {
|
|
64
|
+
[ "$SKIP_BUILD" = true ] && return 0
|
|
65
|
+
need_build || return 0
|
|
66
|
+
local pkg; pkg=$(detect_pkg)
|
|
67
|
+
if [ -z "$pkg" ]; then
|
|
68
|
+
echo "[rotom-up] 未找到包管理器(pnpm/npm/yarn)"; return 1
|
|
69
|
+
fi
|
|
70
|
+
if [ "$DEV_MODE" = true ]; then
|
|
71
|
+
echo "[rotom-up] 构建中 (build, skip dashboard)..."
|
|
72
|
+
case "$pkg" in
|
|
73
|
+
pnpm) (cd "$SCRIPT_DIR" && pnpm build) ;;
|
|
74
|
+
npm) (cd "$SCRIPT_DIR" && npm run build) ;;
|
|
75
|
+
yarn) (cd "$SCRIPT_DIR" && yarn build) ;;
|
|
76
|
+
esac
|
|
77
|
+
else
|
|
78
|
+
echo "[rotom-up] 构建中 (build:master)..."
|
|
79
|
+
case "$pkg" in
|
|
80
|
+
pnpm) (cd "$SCRIPT_DIR" && pnpm build:master) ;;
|
|
81
|
+
npm) (cd "$SCRIPT_DIR" && npm run build:master) ;;
|
|
82
|
+
yarn) (cd "$SCRIPT_DIR" && yarn build:master) ;;
|
|
83
|
+
esac
|
|
84
|
+
fi
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# 校验 PID 文件指向的进程是否仍存活
|
|
88
|
+
is_pid_alive() {
|
|
89
|
+
local pid_file="$1"
|
|
90
|
+
[ -f "$pid_file" ] || return 1
|
|
91
|
+
local pid; pid=$(cat "$pid_file" 2>/dev/null || true)
|
|
92
|
+
[ -z "$pid" ] && return 1
|
|
93
|
+
kill -0 "$pid" 2>/dev/null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# 优雅停止 PID 文件指向的进程(SIGTERM → 等 5s → SIGKILL)
|
|
97
|
+
stop_pid_file() {
|
|
98
|
+
local pid_file="$1" label="$2"
|
|
99
|
+
if ! is_pid_alive "$pid_file"; then
|
|
100
|
+
rm -f "$pid_file"
|
|
101
|
+
return 1
|
|
102
|
+
fi
|
|
103
|
+
local pid; pid=$(cat "$pid_file")
|
|
104
|
+
echo "[rotom-up] 停止 $label (PID $pid)..."
|
|
105
|
+
kill "$pid" 2>/dev/null || true
|
|
106
|
+
local i=0
|
|
107
|
+
while kill -0 "$pid" 2>/dev/null && [ $i -lt 50 ]; do
|
|
108
|
+
sleep 0.1; i=$((i+1))
|
|
109
|
+
done
|
|
110
|
+
kill -0 "$pid" 2>/dev/null && kill -9 "$pid" 2>/dev/null || true
|
|
111
|
+
rm -f "$pid_file"
|
|
112
|
+
return 0
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# 等 master 端口就绪(默认 10s)
|
|
116
|
+
wait_master_ready() {
|
|
117
|
+
local max_wait=${1:-10} i=0
|
|
118
|
+
while [ $i -lt $max_wait ]; do
|
|
119
|
+
if curl -sf --connect-timeout 1 "http://127.0.0.1:$PORT/health" >/dev/null 2>&1; then
|
|
120
|
+
return 0
|
|
121
|
+
fi
|
|
122
|
+
sleep 1; i=$((i+1))
|
|
123
|
+
done
|
|
124
|
+
return 1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# 校验 Claude Code CLI 已安装(默认执行器依赖它)
|
|
128
|
+
ensure_claude_installed() {
|
|
129
|
+
if command -v claude &>/dev/null; then
|
|
130
|
+
return 0
|
|
131
|
+
fi
|
|
132
|
+
echo "[rotom-up] ❌ 未检测到 Claude Code CLI (\`claude\`)"
|
|
133
|
+
echo ""
|
|
134
|
+
echo " Rotom 默认以 Claude Code 作为执行器,请先安装:"
|
|
135
|
+
echo " npm install -g @anthropic-ai/claude-code"
|
|
136
|
+
echo " 详细文档: https://docs.claude.com/en/docs/claude-code"
|
|
137
|
+
return 1
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# 在 master 已就绪的前提下,自动创建「默认公司」+ 注册 Claude Code worker,
|
|
141
|
+
# 写入 executor.config.json,并把 rotom CLI 的 defaultAgent 指向该 worker。
|
|
142
|
+
bootstrap_default_worker() {
|
|
143
|
+
local domain="默认公司"
|
|
144
|
+
local short_host
|
|
145
|
+
short_host=$(hostname -s 2>/dev/null | tr -cd '[:alnum:]_-' || echo local)
|
|
146
|
+
[ -z "$short_host" ] && short_host=local
|
|
147
|
+
local base="http://127.0.0.1:$PORT/api"
|
|
148
|
+
|
|
149
|
+
# 1. 创建默认公司(201 新建 / 409 已存在 均视为成功)
|
|
150
|
+
local domain_code
|
|
151
|
+
domain_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "$base/domains" \
|
|
152
|
+
-H 'Content-Type: application/json' \
|
|
153
|
+
--data-raw "{\"name\":\"$domain\",\"description\":\"默认公司\"}")
|
|
154
|
+
case "$domain_code" in
|
|
155
|
+
201|409) ;;
|
|
156
|
+
*) echo "[rotom-up] ❌ 创建默认公司失败 (HTTP $domain_code)"; return 1 ;;
|
|
157
|
+
esac
|
|
158
|
+
|
|
159
|
+
# 2. 注册 worker;若 409 冲突则追加序号重试
|
|
160
|
+
local base_name="claude-$short_host" try_name="" token=""
|
|
161
|
+
for attempt in 1 2 3 4 5; do
|
|
162
|
+
local name="$base_name"
|
|
163
|
+
[ $attempt -gt 1 ] && name="$base_name-$attempt"
|
|
164
|
+
local body_file; body_file=$(mktemp)
|
|
165
|
+
local code
|
|
166
|
+
code=$(curl -s -o "$body_file" -w '%{http_code}' -X POST "$base/agents" \
|
|
167
|
+
-H 'Content-Type: application/json' \
|
|
168
|
+
--data-raw "{\"name\":\"$name\",\"domain\":\"$domain\"}")
|
|
169
|
+
if [ "$code" = "201" ]; then
|
|
170
|
+
token=$(sed -nE 's/.*"token"[[:space:]]*:[[:space:]]*"(mesh_[^"]+)".*/\1/p' "$body_file")
|
|
171
|
+
try_name="$name"
|
|
172
|
+
rm -f "$body_file"
|
|
173
|
+
break
|
|
174
|
+
fi
|
|
175
|
+
local resp; resp=$(cat "$body_file"); rm -f "$body_file"
|
|
176
|
+
[ "$code" = "409" ] && continue
|
|
177
|
+
echo "[rotom-up] ❌ 注册 worker 失败 (HTTP $code): $resp"
|
|
178
|
+
return 1
|
|
179
|
+
done
|
|
180
|
+
if [ -z "$token" ]; then
|
|
181
|
+
echo "[rotom-up] ❌ 注册 worker 失败:名字 \"$base_name\" 多次冲突"
|
|
182
|
+
return 1
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# 3. 写入 executor.config.json
|
|
186
|
+
cat > "$EXECUTOR_CONFIG" <<EOF
|
|
187
|
+
{
|
|
188
|
+
"master": "ws://localhost:$PORT",
|
|
189
|
+
"workers": [
|
|
190
|
+
{ "name": "$try_name", "token": "$token", "cliTool": "claude" }
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
EOF
|
|
194
|
+
|
|
195
|
+
# 4. 设置 rotom CLI 默认身份(不覆盖已有配置)
|
|
196
|
+
local rotom_cfg="$ROTOM_HOME/config.json"
|
|
197
|
+
if [ ! -f "$rotom_cfg" ]; then
|
|
198
|
+
cat > "$rotom_cfg" <<EOF
|
|
199
|
+
{
|
|
200
|
+
"defaultAgent": "$try_name",
|
|
201
|
+
"agents": {}
|
|
202
|
+
}
|
|
203
|
+
EOF
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
echo "[rotom-up] ✅ 已注册默认 worker: $try_name (domain=$domain)"
|
|
207
|
+
echo " executor 配置: $EXECUTOR_CONFIG"
|
|
208
|
+
echo " rotom CLI 默认身份: $try_name"
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# 若 `rotom` 不在 PATH 中,尝试用 pnpm link --global 让它全局可用
|
|
212
|
+
ensure_rotom_on_path() {
|
|
213
|
+
if command -v rotom &>/dev/null; then
|
|
214
|
+
local current; current=$(command -v rotom)
|
|
215
|
+
local target; target=$(readlink "$current" 2>/dev/null || echo "$current")
|
|
216
|
+
# 同一仓库的 bin/rotom,无需重复链接
|
|
217
|
+
if [ "$target" = "$SCRIPT_DIR/bin/rotom" ] || [ "$current" = "$SCRIPT_DIR/bin/rotom" ]; then
|
|
218
|
+
return 0
|
|
219
|
+
fi
|
|
220
|
+
fi
|
|
221
|
+
echo "[rotom-up] 注册 rotom CLI 为全局命令..."
|
|
222
|
+
if command -v pnpm &>/dev/null && (cd "$SCRIPT_DIR" && pnpm link --global >/dev/null 2>&1); then
|
|
223
|
+
echo "[rotom-up] ✅ rotom 已全局可用"
|
|
224
|
+
return 0
|
|
225
|
+
fi
|
|
226
|
+
echo "[rotom-up] ⚠️ 自动注册失败,可手动执行任一命令:"
|
|
227
|
+
echo " cd $SCRIPT_DIR && pnpm link --global"
|
|
228
|
+
echo " ln -s $SCRIPT_DIR/bin/rotom /usr/local/bin/rotom"
|
|
229
|
+
return 0
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# ── 命令 ──────────────────────────────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
do_start() {
|
|
235
|
+
fix_path || true
|
|
236
|
+
|
|
237
|
+
# 0. 校验 Claude Code 已安装(默认执行器依赖它)
|
|
238
|
+
ensure_claude_installed || return 1
|
|
239
|
+
|
|
240
|
+
# 1. 已经在跑就别重复起
|
|
241
|
+
if is_pid_alive "$MASTER_PID" && is_pid_alive "$EXECUTOR_PID"; then
|
|
242
|
+
echo "[rotom-up] 已在运行 (master PID $(cat "$MASTER_PID"), executor PID $(cat "$EXECUTOR_PID"))"
|
|
243
|
+
return 0
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
# 2. 构建
|
|
247
|
+
ensure_built
|
|
248
|
+
|
|
249
|
+
mkdir -p "$ROTOM_HOME" "$RUN_DIR" "$LOG_DIR" "$DATA"
|
|
250
|
+
|
|
251
|
+
# 3. 启 Master
|
|
252
|
+
if is_pid_alive "$MASTER_PID"; then
|
|
253
|
+
echo "[rotom-up] master 已在运行 (PID $(cat "$MASTER_PID")),跳过"
|
|
254
|
+
else
|
|
255
|
+
# 端口预检:避免 6月 14 那次悄无声息的 EADDRINUSE 崩 master。
|
|
256
|
+
# 检测到非自己 pid 占着端口时,把占用的 PID/进程报出来,让用户/脚本
|
|
257
|
+
# 能立刻看出"旧 master 没死干净"vs"别的程序占着端口"。
|
|
258
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
259
|
+
local occupier
|
|
260
|
+
occupier=$(lsof -nP -iTCP:"$PORT" -sTCP:LISTEN -t 2>/dev/null | head -n1 || true)
|
|
261
|
+
if [ -n "$occupier" ]; then
|
|
262
|
+
local occupier_cmd
|
|
263
|
+
occupier_cmd=$(ps -p "$occupier" -o command= 2>/dev/null | head -c 120 || echo "(unknown)")
|
|
264
|
+
echo "[rotom-up] ❌ 端口 $PORT 已被占用 (PID $occupier: $occupier_cmd)"
|
|
265
|
+
echo "[rotom-up] 如需强制接管,先 \`lsof -nP -iTCP:$PORT -sTCP:LISTEN\` 找到占用进程并 stop,或 \`rotom-up stop\` 清理旧 master"
|
|
266
|
+
return 1
|
|
267
|
+
fi
|
|
268
|
+
fi
|
|
269
|
+
echo "[rotom-up] 启动 master..."
|
|
270
|
+
nohup node "$MASTER_JS" --port "$PORT" --host "$HOST" --data "$DATA" \
|
|
271
|
+
>> "$MASTER_LOG" 2>&1 &
|
|
272
|
+
echo "$!" > "$MASTER_PID"
|
|
273
|
+
if ! wait_master_ready 15; then
|
|
274
|
+
echo "[rotom-up] ❌ master 启动失败,查看 $MASTER_LOG"
|
|
275
|
+
stop_pid_file "$MASTER_PID" master || true
|
|
276
|
+
return 1
|
|
277
|
+
fi
|
|
278
|
+
echo "[rotom-up] ✅ master 就绪 (PID $(cat "$MASTER_PID"), http://$HOST:$PORT)"
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
# 4. Executor 由 master 自动 spawn(Phase 1 OPC 行为)。
|
|
282
|
+
# master 启动时如果检测到 ~/.rotom/executor.config.json 不存在,
|
|
283
|
+
# 会自动生成 .auto-executor.json 并 spawn 本机 executor 子进程,
|
|
284
|
+
# master 退出时一并 kill。这里不再单独启动 executor。
|
|
285
|
+
if [ -f "$EXECUTOR_CONFIG" ]; then
|
|
286
|
+
echo "[rotom-up] 检测到 $EXECUTOR_CONFIG — master 将尊重用户配置,不会自动 spawn"
|
|
287
|
+
else
|
|
288
|
+
echo "[rotom-up] executor 将由 master 自动 spawn(OPC 模式,免 token 配置)"
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
# 5. 让 rotom CLI 全局可用
|
|
292
|
+
ensure_rotom_on_path
|
|
293
|
+
|
|
294
|
+
if [ "$DEV_MODE" != true ]; then
|
|
295
|
+
echo ""
|
|
296
|
+
echo " Dashboard: http://localhost:$PORT/dashboard"
|
|
297
|
+
echo " Logs: $LOG_DIR/{master,executor}.log"
|
|
298
|
+
echo " Stop: pnpm stop"
|
|
299
|
+
fi
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
do_stop() {
|
|
303
|
+
local stopped=false
|
|
304
|
+
stop_pid_file "$EXECUTOR_PID" executor && stopped=true || true
|
|
305
|
+
stop_pid_file "$MASTER_PID" master && stopped=true || true
|
|
306
|
+
if [ "$stopped" = true ]; then
|
|
307
|
+
echo "[rotom-up] 已停止"
|
|
308
|
+
else
|
|
309
|
+
echo "[rotom-up] 未在运行"
|
|
310
|
+
fi
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
do_restart() {
|
|
314
|
+
do_stop || true
|
|
315
|
+
sleep 1
|
|
316
|
+
do_start
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
do_status() {
|
|
320
|
+
local any=false
|
|
321
|
+
if is_pid_alive "$MASTER_PID"; then
|
|
322
|
+
echo "[rotom-up] master 运行中 (PID $(cat "$MASTER_PID"), http://$HOST:$PORT)"
|
|
323
|
+
any=true
|
|
324
|
+
else
|
|
325
|
+
echo "[rotom-up] master 未运行"
|
|
326
|
+
fi
|
|
327
|
+
if is_pid_alive "$EXECUTOR_PID"; then
|
|
328
|
+
echo "[rotom-up] executor 运行中 (PID $(cat "$EXECUTOR_PID"))"
|
|
329
|
+
any=true
|
|
330
|
+
else
|
|
331
|
+
echo "[rotom-up] executor 未运行"
|
|
332
|
+
fi
|
|
333
|
+
[ "$any" = true ] || return 1
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
do_logs() {
|
|
337
|
+
mkdir -p "$LOG_DIR"
|
|
338
|
+
touch "$MASTER_LOG" "$EXECUTOR_LOG"
|
|
339
|
+
echo "[rotom-up] tail -F $MASTER_LOG $EXECUTOR_LOG (Ctrl+C 退出)"
|
|
340
|
+
exec tail -F "$MASTER_LOG" "$EXECUTOR_LOG"
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
do_start_dev() {
|
|
344
|
+
# 1. Start master + executor as daemons (reuse do_start logic)
|
|
345
|
+
DEV_MODE=true do_start || return 1
|
|
346
|
+
|
|
347
|
+
# 2. Start Vite dev server in foreground
|
|
348
|
+
local pkg; pkg=$(detect_pkg)
|
|
349
|
+
if [ -z "$pkg" ]; then
|
|
350
|
+
echo "[rotom-up] 未找到包管理器"; return 1
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
echo "[rotom-up] 启动 Vite dev server (hot reload)..."
|
|
354
|
+
echo ""
|
|
355
|
+
echo " Frontend: http://localhost:3000"
|
|
356
|
+
echo " Backend: http://localhost:$PORT"
|
|
357
|
+
echo " Ctrl+C 停止所有服务"
|
|
358
|
+
echo ""
|
|
359
|
+
|
|
360
|
+
# Cleanup background processes on exit
|
|
361
|
+
dev_cleanup() {
|
|
362
|
+
echo ""
|
|
363
|
+
echo "[rotom-up] 停止所有服务..."
|
|
364
|
+
stop_pid_file "$EXECUTOR_PID" executor 2>/dev/null || true
|
|
365
|
+
stop_pid_file "$MASTER_PID" master 2>/dev/null || true
|
|
366
|
+
exit 0
|
|
367
|
+
}
|
|
368
|
+
trap dev_cleanup INT TERM
|
|
369
|
+
|
|
370
|
+
case "$pkg" in
|
|
371
|
+
pnpm) exec pnpm --filter @a2a-gateway/dashboard dev ;;
|
|
372
|
+
npm) exec npm run --prefix "$SCRIPT_DIR/packages/dashboard" dev ;;
|
|
373
|
+
yarn) exec yarn --cwd "$SCRIPT_DIR/packages/dashboard" dev ;;
|
|
374
|
+
esac
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
do_help() {
|
|
378
|
+
cat <<EOF
|
|
379
|
+
Rotom 一站式启停 —— Master(含 Dashboard)+ Executor 守护进程
|
|
380
|
+
|
|
381
|
+
用法: rotom-up <command> [options]
|
|
382
|
+
|
|
383
|
+
命令:
|
|
384
|
+
start 启动 master + executor(守护进程,自动 build)
|
|
385
|
+
stop 停止所有进程
|
|
386
|
+
restart 重启
|
|
387
|
+
status 查看运行状态
|
|
388
|
+
logs tail -F 两个日志
|
|
389
|
+
|
|
390
|
+
选项:
|
|
391
|
+
--port, -p <port> Master 端口 (默认: $PORT)
|
|
392
|
+
--host <addr> Master 监听地址 (默认: $HOST)
|
|
393
|
+
--data, -d <dir> Master 数据目录 (默认: $DATA)
|
|
394
|
+
--no-build 跳过构建检查直接启动
|
|
395
|
+
--dev 前端开发模式:Vite dev server + hot reload (localhost:3000)
|
|
396
|
+
|
|
397
|
+
运行时目录:
|
|
398
|
+
PID: $RUN_DIR/{master,executor}.pid
|
|
399
|
+
日志: $LOG_DIR/{master,executor}.log
|
|
400
|
+
配置: $EXECUTOR_CONFIG
|
|
401
|
+
EOF
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
# ── 入口 ──────────────────────────────────────────────────────────────────────
|
|
405
|
+
|
|
406
|
+
CMD="${1:-help}"; shift 2>/dev/null || true
|
|
407
|
+
|
|
408
|
+
while [[ $# -gt 0 ]]; do
|
|
409
|
+
case "$1" in
|
|
410
|
+
--port|-p) PORT="$2"; shift 2 ;;
|
|
411
|
+
--host) HOST="$2"; shift 2 ;;
|
|
412
|
+
--data|-d) DATA="$2"; shift 2 ;;
|
|
413
|
+
--no-build) SKIP_BUILD=true; shift ;;
|
|
414
|
+
--dev) DEV_MODE=true; shift ;;
|
|
415
|
+
--help|-h) do_help; exit 0 ;;
|
|
416
|
+
*) echo "未知选项: $1"; do_help; exit 1 ;;
|
|
417
|
+
esac
|
|
418
|
+
done
|
|
419
|
+
|
|
420
|
+
case "$CMD" in
|
|
421
|
+
start) [ "$DEV_MODE" = true ] && do_start_dev || do_start ;;
|
|
422
|
+
stop) do_stop ;;
|
|
423
|
+
restart) do_restart ;;
|
|
424
|
+
status) do_status ;;
|
|
425
|
+
logs) do_logs ;;
|
|
426
|
+
help|-h|--help) do_help ;;
|
|
427
|
+
*) echo "未知命令: $CMD"; do_help; exit 1 ;;
|
|
428
|
+
esac
|
package/dist/cli/ask.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { toBeijing } from "../shared/time.js";
|
|
2
|
+
/**
|
|
3
|
+
* rotom ask —— ask-bridge 查询/取消(提问用 #reply 标记,系统自动建 bridge)。
|
|
4
|
+
*
|
|
5
|
+
* 子命令:
|
|
6
|
+
* list --group <gid> [--status pending|answered|timed_out|cancelled]
|
|
7
|
+
* show <bridgeId>
|
|
8
|
+
* cancel <bridgeId>
|
|
9
|
+
*/
|
|
10
|
+
import { api, printJson, printTable, fail, flagStr, pretty, } from "./common.js";
|
|
11
|
+
import { route, qs, usage } from "./routes.js";
|
|
12
|
+
function formatBridgeRow(b) {
|
|
13
|
+
return {
|
|
14
|
+
id: b.id.slice(0, 8),
|
|
15
|
+
group: b.group_id.slice(0, 8),
|
|
16
|
+
asker: b.asker,
|
|
17
|
+
target: b.target,
|
|
18
|
+
status: b.status,
|
|
19
|
+
escalate_to: b.escalate_to || "-",
|
|
20
|
+
created: b.created_at ? toBeijing(b.created_at).slice(11, 19) : "-",
|
|
21
|
+
expires: b.expires_at ? toBeijing(b.expires_at).slice(11, 19) : "-",
|
|
22
|
+
reply_msg: b.reply_msg_id ?? "-",
|
|
23
|
+
issue: b.issue_id ? b.issue_id.slice(0, 8) : "-",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export async function cmdAsk(agent, rest, flags) {
|
|
27
|
+
const sub = rest[0];
|
|
28
|
+
if (sub === "list") {
|
|
29
|
+
const gid = flagStr(flags, "group");
|
|
30
|
+
const status = flagStr(flags, "status");
|
|
31
|
+
// /groups 列群不列 bridge;bridge 走 /groups/:id/asks
|
|
32
|
+
if (!gid)
|
|
33
|
+
usage("ask list", "--group <id> [--status pending|answered|timed_out|cancelled]");
|
|
34
|
+
const data = await api(agent, "GET", `${route("/groups/:groupId/asks", gid)}${qs({ status })}`);
|
|
35
|
+
if (pretty) {
|
|
36
|
+
printTable(data.map(formatBridgeRow), ["id", "group", "asker", "target", "status", "escalate_to", "created", "expires", "reply_msg", "issue"]);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
printJson(data);
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (sub === "show") {
|
|
44
|
+
const id = rest[1];
|
|
45
|
+
if (!id)
|
|
46
|
+
usage("ask show", "<bridgeId>");
|
|
47
|
+
const data = await api(agent, "GET", route("/asks/:id", id));
|
|
48
|
+
printJson(data);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (sub === "cancel") {
|
|
52
|
+
const id = rest[1];
|
|
53
|
+
if (!id)
|
|
54
|
+
usage("ask cancel", "<bridgeId>");
|
|
55
|
+
const data = await api(agent, "POST", route("/asks/:id/cancel", id));
|
|
56
|
+
printJson(data);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// ask 子命令已废弃——提问用 #reply 标记(系统自动建 bridge)。
|
|
60
|
+
// 保留 list/show/cancel 供 agent 主动查询/取消。
|
|
61
|
+
fail(`rotom ask 子命令已废弃。提问直接在回复里 @ 对方 + #reply 标记,系统自动起 timer。\n可用:rotom ask list | show <id> | cancel <id>`);
|
|
62
|
+
}
|