@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.
Files changed (189) hide show
  1. package/README.md +417 -0
  2. package/bin/mesh-master.sh +439 -0
  3. package/bin/rotom +29 -0
  4. package/bin/rotom-link.sh +136 -0
  5. package/bin/rotom-send-with-status +57 -0
  6. package/bin/rotom-up.sh +428 -0
  7. package/dist/cli/ask.js +62 -0
  8. package/dist/cli/common.js +321 -0
  9. package/dist/cli/config.js +65 -0
  10. package/dist/cli/directory.js +17 -0
  11. package/dist/cli/executor.js +58 -0
  12. package/dist/cli/fed.js +91 -0
  13. package/dist/cli/group.js +273 -0
  14. package/dist/cli/identity.js +62 -0
  15. package/dist/cli/init.js +268 -0
  16. package/dist/cli/issue.js +202 -0
  17. package/dist/cli/join.js +170 -0
  18. package/dist/cli/link.js +47 -0
  19. package/dist/cli/master.js +51 -0
  20. package/dist/cli/memory.js +307 -0
  21. package/dist/cli/note.js +68 -0
  22. package/dist/cli/repo.js +77 -0
  23. package/dist/cli/rotom.js +277 -0
  24. package/dist/cli/routes.js +118 -0
  25. package/dist/cli/run.js +45 -0
  26. package/dist/cli/schedule.js +237 -0
  27. package/dist/cli/skill.js +173 -0
  28. package/dist/cli/team.js +106 -0
  29. package/dist/executor/claude-code-hook.cjs +80 -0
  30. package/dist/executor/cli-executor.js +8 -0
  31. package/dist/executor/executors/claude-code.js +780 -0
  32. package/dist/executor/executors/codex.js +719 -0
  33. package/dist/executor/executors/hermes-cli.js +855 -0
  34. package/dist/executor/executors/openclaw.js +467 -0
  35. package/dist/executor/executors/pi.js +514 -0
  36. package/dist/executor/index.js +269 -0
  37. package/dist/executor/jsonrpc-transport.js +125 -0
  38. package/dist/executor/process-runner.js +101 -0
  39. package/dist/executor/reasoning-status.js +83 -0
  40. package/dist/executor/repo-cache.js +502 -0
  41. package/dist/executor/session-store.js +188 -0
  42. package/dist/executor/worker-chat.js +257 -0
  43. package/dist/executor/worker-connection.js +89 -0
  44. package/dist/executor/worker-issue.js +264 -0
  45. package/dist/executor/worker.js +877 -0
  46. package/dist/link/pending-requests.js +72 -0
  47. package/dist/link/server.js +233 -0
  48. package/dist/link/visibility-store.js +58 -0
  49. package/dist/master/api/agents.js +333 -0
  50. package/dist/master/api/artifacts.js +271 -0
  51. package/dist/master/api/domains.js +64 -0
  52. package/dist/master/api/groups.js +635 -0
  53. package/dist/master/api/guidance-templates.js +147 -0
  54. package/dist/master/api/index.js +89 -0
  55. package/dist/master/api/issues-patrol.js +172 -0
  56. package/dist/master/api/issues.js +663 -0
  57. package/dist/master/api/links-patrol.js +168 -0
  58. package/dist/master/api/links.js +114 -0
  59. package/dist/master/api/memory.js +259 -0
  60. package/dist/master/api/messages.js +157 -0
  61. package/dist/master/api/notes.js +77 -0
  62. package/dist/master/api/schedule-patterns.js +133 -0
  63. package/dist/master/api/schedules.js +272 -0
  64. package/dist/master/api/sessions.js +158 -0
  65. package/dist/master/api/share.js +269 -0
  66. package/dist/master/api/skills.js +190 -0
  67. package/dist/master/api/teams.js +122 -0
  68. package/dist/master/api/uploads.js +245 -0
  69. package/dist/master/auth.js +134 -0
  70. package/dist/master/dashboard/animations/calico-dozing.apng +0 -0
  71. package/dist/master/dashboard/animations/calico-error.apng +0 -0
  72. package/dist/master/dashboard/animations/calico-happy.apng +0 -0
  73. package/dist/master/dashboard/animations/calico-notification.apng +0 -0
  74. package/dist/master/dashboard/animations/calico-sleeping.apng +0 -0
  75. package/dist/master/dashboard/animations/calico-thinking.apng +0 -0
  76. package/dist/master/dashboard/animations/calico-waking.apng +0 -0
  77. package/dist/master/dashboard/assets/ApprovalCard-C38VV6ko.css +1 -0
  78. package/dist/master/dashboard/assets/ApprovalCard-CHPh2dmE.js +17 -0
  79. package/dist/master/dashboard/assets/ArtifactPanel-P_2gAP7v.js +1 -0
  80. package/dist/master/dashboard/assets/ArtifactPanel-aGHySny5.css +1 -0
  81. package/dist/master/dashboard/assets/css.worker-DaIe3gwK.js +84 -0
  82. package/dist/master/dashboard/assets/editor.worker-BCzxt1at.js +12 -0
  83. package/dist/master/dashboard/assets/html.worker-CKrFyw_2.js +461 -0
  84. package/dist/master/dashboard/assets/index-CChrTn81.css +32 -0
  85. package/dist/master/dashboard/assets/index-Dhu4SN1z.js +181 -0
  86. package/dist/master/dashboard/assets/json.worker-B7c_PmGb.js +49 -0
  87. package/dist/master/dashboard/assets/markdown-CeN5IgdF.js +29 -0
  88. package/dist/master/dashboard/assets/monaco-core-DyX1CsEw.css +1 -0
  89. package/dist/master/dashboard/assets/monaco-core-oQiQUisy.js +833 -0
  90. package/dist/master/dashboard/assets/monaco-setup-CiOPQdmo.js +1 -0
  91. package/dist/master/dashboard/assets/react-vendor-C8IxlyCR.js +67 -0
  92. package/dist/master/dashboard/assets/ts.worker-BhkL8olL.js +51334 -0
  93. package/dist/master/dashboard/assets/useMonaco-ILb4vyPh.js +12 -0
  94. package/dist/master/dashboard/assets/vite-preload-CxJPbCTl.js +1 -0
  95. package/dist/master/dashboard/debug-auth.html +197 -0
  96. package/dist/master/dashboard/favicon.ico +0 -0
  97. package/dist/master/dashboard/index.html +20 -0
  98. package/dist/master/dashboard/rotom-avatar.png +0 -0
  99. package/dist/master/db/agent-sessions.js +60 -0
  100. package/dist/master/db/agent-visibility.js +64 -0
  101. package/dist/master/db/agents.js +119 -0
  102. package/dist/master/db/ask-bridges.js +157 -0
  103. package/dist/master/db/build-update.js +59 -0
  104. package/dist/master/db/core.js +82 -0
  105. package/dist/master/db/domains.js +80 -0
  106. package/dist/master/db/groups.js +316 -0
  107. package/dist/master/db/guidance-templates.js +58 -0
  108. package/dist/master/db/index.js +12 -0
  109. package/dist/master/db/internal.js +45 -0
  110. package/dist/master/db/issues-patrol.js +81 -0
  111. package/dist/master/db/issues.js +373 -0
  112. package/dist/master/db/links.js +221 -0
  113. package/dist/master/db/master-node.js +43 -0
  114. package/dist/master/db/memory.js +272 -0
  115. package/dist/master/db/messages.js +210 -0
  116. package/dist/master/db/notes.js +55 -0
  117. package/dist/master/db/schedule-patterns.js +56 -0
  118. package/dist/master/db/schedules.js +135 -0
  119. package/dist/master/db/skills.js +144 -0
  120. package/dist/master/db/team.js +88 -0
  121. package/dist/master/db/types.js +10 -0
  122. package/dist/master/db.js +12 -0
  123. package/dist/master/embedded.js +133 -0
  124. package/dist/master/federation/client.js +283 -0
  125. package/dist/master/federation/identity.js +133 -0
  126. package/dist/master/federation/manager.js +267 -0
  127. package/dist/master/federation/publisher.js +87 -0
  128. package/dist/master/federation/self-publisher.js +69 -0
  129. package/dist/master/federation/server.js +487 -0
  130. package/dist/master/group-paths.js +208 -0
  131. package/dist/master/offline-queue.js +38 -0
  132. package/dist/master/opc-bootstrap.js +245 -0
  133. package/dist/master/patrol-terminal.js +275 -0
  134. package/dist/master/repo-scan.js +188 -0
  135. package/dist/master/router.js +214 -0
  136. package/dist/master/scheduler-handlers.js +510 -0
  137. package/dist/master/scheduler.js +201 -0
  138. package/dist/master/server.js +203 -0
  139. package/dist/master/services/link-collector.js +82 -0
  140. package/dist/master/services/link-patrol-bootstrap.js +50 -0
  141. package/dist/master/services/memory-extract-prompt.js +34 -0
  142. package/dist/master/services/patrol-bootstrap.js +63 -0
  143. package/dist/master/share-tokens.js +56 -0
  144. package/dist/master/terminal-hub.js +300 -0
  145. package/dist/master/uploads.js +108 -0
  146. package/dist/master/util/fs.js +100 -0
  147. package/dist/master/util/paths.js +50 -0
  148. package/dist/master/util/persona.js +10 -0
  149. package/dist/master/ws-hub/connection.js +928 -0
  150. package/dist/master/ws-hub/conversation.js +290 -0
  151. package/dist/master/ws-hub/directory.js +70 -0
  152. package/dist/master/ws-hub/dispatch-enrich.js +34 -0
  153. package/dist/master/ws-hub/hub.js +136 -0
  154. package/dist/master/ws-hub/index.js +9 -0
  155. package/dist/master/ws-hub/internal.js +35 -0
  156. package/dist/master/ws-hub/routing.js +295 -0
  157. package/dist/master/ws-hub/sessions.js +130 -0
  158. package/dist/master/ws-hub.js +11 -0
  159. package/dist/shared/agent-profile.js +44 -0
  160. package/dist/shared/constants.js +55 -0
  161. package/dist/shared/dedup.js +33 -0
  162. package/dist/shared/group-context.js +62 -0
  163. package/dist/shared/json-codec.js +33 -0
  164. package/dist/shared/logger.js +136 -0
  165. package/dist/shared/mention.js +22 -0
  166. package/dist/shared/network.js +40 -0
  167. package/dist/shared/parse.js +18 -0
  168. package/dist/shared/prompt-composer.js +171 -0
  169. package/dist/shared/protocol/client-messages.js +8 -0
  170. package/dist/shared/protocol/enums.js +6 -0
  171. package/dist/shared/protocol/federation.js +62 -0
  172. package/dist/shared/protocol/guards.js +87 -0
  173. package/dist/shared/protocol/server-messages.js +8 -0
  174. package/dist/shared/protocol/types.js +8 -0
  175. package/dist/shared/protocol.js +19 -0
  176. package/dist/shared/readonly-allowlist.js +122 -0
  177. package/dist/shared/rotom-cli-prompt.js +23 -0
  178. package/dist/shared/skill-context.js +19 -0
  179. package/dist/shared/skill-md.js +43 -0
  180. package/dist/shared/slash-commands.js +50 -0
  181. package/dist/shared/time.js +80 -0
  182. package/dist/shared/title.js +46 -0
  183. package/dist/shared/url-extractor.js +99 -0
  184. package/migrations/001-schema.sql +942 -0
  185. package/package.json +68 -0
  186. package/scripts/fix-node-pty-perms.mjs +46 -0
  187. package/skill/rotom-a2a-communicate/SKILL.md +257 -0
  188. package/skill/rotom-bus-host/SKILL.md +78 -0
  189. package/skill/rotom-bus-host/scripts/poll-replies.sh +148 -0
@@ -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
@@ -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
+ }