@silicaclaw/cli 1.0.0-beta.2 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/INSTALL.md +36 -0
- package/README.md +40 -0
- package/apps/local-console/public/index.html +81 -63
- package/apps/local-console/src/server.ts +41 -21
- package/docs/CLOUDFLARE_RELAY.md +61 -0
- package/package.json +6 -1
- package/packages/core/dist/crypto.d.ts +6 -0
- package/packages/core/dist/crypto.js +50 -0
- package/packages/core/dist/directory.d.ts +17 -0
- package/packages/core/dist/directory.js +145 -0
- package/packages/core/dist/identity.d.ts +2 -0
- package/packages/core/dist/identity.js +18 -0
- package/packages/core/dist/index.d.ts +11 -0
- package/packages/core/dist/index.js +27 -0
- package/packages/core/dist/indexing.d.ts +6 -0
- package/packages/core/dist/indexing.js +43 -0
- package/packages/core/dist/presence.d.ts +4 -0
- package/packages/core/dist/presence.js +23 -0
- package/packages/core/dist/profile.d.ts +4 -0
- package/packages/core/dist/profile.js +39 -0
- package/packages/core/dist/publicProfileSummary.d.ts +70 -0
- package/packages/core/dist/publicProfileSummary.js +103 -0
- package/packages/core/dist/socialConfig.d.ts +99 -0
- package/packages/core/dist/socialConfig.js +288 -0
- package/packages/core/dist/socialResolver.d.ts +46 -0
- package/packages/core/dist/socialResolver.js +237 -0
- package/packages/core/dist/socialTemplate.d.ts +2 -0
- package/packages/core/dist/socialTemplate.js +88 -0
- package/packages/core/dist/types.d.ts +37 -0
- package/packages/core/dist/types.js +2 -0
- package/packages/core/src/socialConfig.ts +7 -6
- package/packages/core/src/socialResolver.ts +17 -5
- package/packages/network/dist/abstractions/messageEnvelope.d.ts +28 -0
- package/packages/network/dist/abstractions/messageEnvelope.js +36 -0
- package/packages/network/dist/abstractions/peerDiscovery.d.ts +43 -0
- package/packages/network/dist/abstractions/peerDiscovery.js +2 -0
- package/packages/network/dist/abstractions/topicCodec.d.ts +4 -0
- package/packages/network/dist/abstractions/topicCodec.js +2 -0
- package/packages/network/dist/abstractions/transport.d.ts +36 -0
- package/packages/network/dist/abstractions/transport.js +2 -0
- package/packages/network/dist/codec/jsonMessageEnvelopeCodec.d.ts +5 -0
- package/packages/network/dist/codec/jsonMessageEnvelopeCodec.js +24 -0
- package/packages/network/dist/codec/jsonTopicCodec.d.ts +5 -0
- package/packages/network/dist/codec/jsonTopicCodec.js +12 -0
- package/packages/network/dist/discovery/heartbeatPeerDiscovery.d.ts +28 -0
- package/packages/network/dist/discovery/heartbeatPeerDiscovery.js +144 -0
- package/packages/network/dist/index.d.ts +14 -0
- package/packages/network/dist/index.js +30 -0
- package/packages/network/dist/localEventBus.d.ts +9 -0
- package/packages/network/dist/localEventBus.js +47 -0
- package/packages/network/dist/mock.d.ts +8 -0
- package/packages/network/dist/mock.js +24 -0
- package/packages/network/dist/realPreview.d.ts +105 -0
- package/packages/network/dist/realPreview.js +327 -0
- package/packages/network/dist/relayPreview.d.ts +133 -0
- package/packages/network/dist/relayPreview.js +320 -0
- package/packages/network/dist/transport/udpLanBroadcastTransport.d.ts +23 -0
- package/packages/network/dist/transport/udpLanBroadcastTransport.js +153 -0
- package/packages/network/dist/types.d.ts +6 -0
- package/packages/network/dist/types.js +2 -0
- package/packages/network/dist/webrtcPreview.d.ts +163 -0
- package/packages/network/dist/webrtcPreview.js +844 -0
- package/packages/network/src/index.ts +1 -0
- package/packages/network/src/relayPreview.ts +425 -0
- package/packages/storage/dist/index.d.ts +3 -0
- package/packages/storage/dist/index.js +19 -0
- package/packages/storage/dist/jsonRepo.d.ts +7 -0
- package/packages/storage/dist/jsonRepo.js +29 -0
- package/packages/storage/dist/repos.d.ts +21 -0
- package/packages/storage/dist/repos.js +41 -0
- package/packages/storage/dist/socialRuntimeRepo.d.ts +5 -0
- package/packages/storage/dist/socialRuntimeRepo.js +52 -0
- package/packages/storage/src/socialRuntimeRepo.ts +3 -3
- package/packages/storage/tsconfig.json +6 -1
- package/scripts/quickstart.sh +286 -20
- package/scripts/silicaclaw-cli.mjs +271 -1
- package/scripts/silicaclaw-gateway.mjs +411 -0
- package/scripts/webrtc-signaling-server.mjs +52 -1
package/scripts/quickstart.sh
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
+
INVOKE_PWD="${INIT_CWD:-$PWD}"
|
|
4
5
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
5
6
|
WORK_DIR="$ROOT_DIR"
|
|
6
7
|
IS_NPX_MODE=0
|
|
8
|
+
DEFAULT_MODE_PICK="${QUICKSTART_DEFAULT_MODE:-3}"
|
|
9
|
+
CONNECT_MODE="${QUICKSTART_CONNECT_MODE:-0}"
|
|
10
|
+
|
|
11
|
+
case "$DEFAULT_MODE_PICK" in
|
|
12
|
+
1|2|3) ;;
|
|
13
|
+
*) DEFAULT_MODE_PICK="1" ;;
|
|
14
|
+
esac
|
|
7
15
|
|
|
8
16
|
case "$ROOT_DIR" in
|
|
9
17
|
*"/.npm/_npx/"*)
|
|
@@ -29,6 +37,103 @@ run_cmd() {
|
|
|
29
37
|
eval "$cmd"
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
run_cmd_may_fail() {
|
|
41
|
+
local cmd="$1"
|
|
42
|
+
printf '→ %s\n' "$cmd"
|
|
43
|
+
set +e
|
|
44
|
+
eval "$cmd"
|
|
45
|
+
local code=$?
|
|
46
|
+
set -e
|
|
47
|
+
return $code
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
first_writable_path_dir() {
|
|
51
|
+
local path_value="${PATH:-}"
|
|
52
|
+
local old_ifs="$IFS"
|
|
53
|
+
IFS=':'
|
|
54
|
+
for d in $path_value; do
|
|
55
|
+
if [ -n "$d" ] && [ -d "$d" ] && [ -w "$d" ] && [ -x "$d" ]; then
|
|
56
|
+
printf '%s' "$d"
|
|
57
|
+
IFS="$old_ifs"
|
|
58
|
+
return 0
|
|
59
|
+
fi
|
|
60
|
+
done
|
|
61
|
+
IFS="$old_ifs"
|
|
62
|
+
return 1
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
install_command_shim() {
|
|
66
|
+
local bindir="$1"
|
|
67
|
+
local script_path="$WORK_DIR/scripts/silicaclaw-cli.mjs"
|
|
68
|
+
local target="$bindir/silicaclaw"
|
|
69
|
+
if [ ! -f "$script_path" ]; then
|
|
70
|
+
echo "未找到 CLI 脚本: $script_path"
|
|
71
|
+
return 1
|
|
72
|
+
fi
|
|
73
|
+
cat >"$target" <<EOF
|
|
74
|
+
#!/usr/bin/env bash
|
|
75
|
+
set -euo pipefail
|
|
76
|
+
exec node "$script_path" "\$@"
|
|
77
|
+
EOF
|
|
78
|
+
chmod +x "$target"
|
|
79
|
+
return 0
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
default_system_bin_dir() {
|
|
83
|
+
if [ -d "/usr/local/bin" ]; then
|
|
84
|
+
printf '/usr/local/bin'
|
|
85
|
+
return 0
|
|
86
|
+
fi
|
|
87
|
+
if [ -d "/opt/homebrew/bin" ]; then
|
|
88
|
+
printf '/opt/homebrew/bin'
|
|
89
|
+
return 0
|
|
90
|
+
fi
|
|
91
|
+
printf '/usr/local/bin'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
detect_shell_rc_file() {
|
|
95
|
+
local sh_name="${SHELL:-}"
|
|
96
|
+
case "$sh_name" in
|
|
97
|
+
*/zsh) printf '%s' "$HOME/.zshrc" ;;
|
|
98
|
+
*/bash) printf '%s' "$HOME/.bashrc" ;;
|
|
99
|
+
*)
|
|
100
|
+
if [ -n "${ZSH_VERSION:-}" ]; then
|
|
101
|
+
printf '%s' "$HOME/.zshrc"
|
|
102
|
+
else
|
|
103
|
+
printf '%s' "$HOME/.bashrc"
|
|
104
|
+
fi
|
|
105
|
+
;;
|
|
106
|
+
esac
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
install_npx_alias() {
|
|
110
|
+
local rc_file
|
|
111
|
+
rc_file="$(detect_shell_rc_file)"
|
|
112
|
+
local begin_mark="# >>> silicaclaw npx alias >>>"
|
|
113
|
+
local end_mark="# <<< silicaclaw npx alias <<<"
|
|
114
|
+
local alias_line="alias silicaclaw='npx -y @silicaclaw/cli@beta'"
|
|
115
|
+
|
|
116
|
+
mkdir -p "$(dirname "$rc_file")"
|
|
117
|
+
touch "$rc_file"
|
|
118
|
+
|
|
119
|
+
if grep -Fq "$begin_mark" "$rc_file"; then
|
|
120
|
+
echo "已存在 silicaclaw alias 配置: $rc_file"
|
|
121
|
+
return 0
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
{
|
|
125
|
+
echo ""
|
|
126
|
+
echo "$begin_mark"
|
|
127
|
+
echo "$alias_line"
|
|
128
|
+
echo "$end_mark"
|
|
129
|
+
} >>"$rc_file"
|
|
130
|
+
|
|
131
|
+
echo "已写入 alias 到: $rc_file"
|
|
132
|
+
echo "执行以下命令即可在当前 shell 立即生效:"
|
|
133
|
+
echo "source \"$rc_file\""
|
|
134
|
+
return 0
|
|
135
|
+
}
|
|
136
|
+
|
|
32
137
|
ask_yes_no() {
|
|
33
138
|
local prompt="$1"
|
|
34
139
|
local default="${2:-Y}"
|
|
@@ -50,6 +155,40 @@ pause_continue() {
|
|
|
50
155
|
read -r -p "按回车继续..." _ || true
|
|
51
156
|
}
|
|
52
157
|
|
|
158
|
+
detect_public_ip() {
|
|
159
|
+
local ip=""
|
|
160
|
+
if command -v curl >/dev/null 2>&1; then
|
|
161
|
+
ip="$(curl -fsSL --max-time 3 https://api.ipify.org 2>/dev/null || true)"
|
|
162
|
+
fi
|
|
163
|
+
if [ -z "$ip" ] && command -v curl >/dev/null 2>&1; then
|
|
164
|
+
ip="$(curl -fsSL --max-time 3 https://ifconfig.me 2>/dev/null || true)"
|
|
165
|
+
fi
|
|
166
|
+
if [ -z "$ip" ] && command -v wget >/dev/null 2>&1; then
|
|
167
|
+
ip="$(wget -qO- --timeout=3 https://api.ipify.org 2>/dev/null || true)"
|
|
168
|
+
fi
|
|
169
|
+
ip="$(printf '%s' "$ip" | tr -d '[:space:]')"
|
|
170
|
+
printf '%s' "$ip"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
url_host() {
|
|
174
|
+
local raw="${1:-}"
|
|
175
|
+
if [ -z "$raw" ]; then
|
|
176
|
+
printf ''
|
|
177
|
+
return 0
|
|
178
|
+
fi
|
|
179
|
+
node -e "try{const u=new URL(process.argv[1]); process.stdout.write(u.hostname||'');}catch{process.stdout.write('');}" "$raw"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
url_port_or_default() {
|
|
183
|
+
local raw="${1:-}"
|
|
184
|
+
local fallback="${2:-4510}"
|
|
185
|
+
if [ -z "$raw" ]; then
|
|
186
|
+
printf '%s' "$fallback"
|
|
187
|
+
return 0
|
|
188
|
+
fi
|
|
189
|
+
node -e "try{const u=new URL(process.argv[1]); process.stdout.write(String(u.port || '$fallback'));}catch{process.stdout.write('$fallback');}" "$raw"
|
|
190
|
+
}
|
|
191
|
+
|
|
53
192
|
title "SilicaClaw Quick Start 启动"
|
|
54
193
|
echo "目录: $ROOT_DIR"
|
|
55
194
|
echo "目标: 用终端一步步完成安装与启动(类似 OpenClaw Quick Start)"
|
|
@@ -57,7 +196,7 @@ pause_continue
|
|
|
57
196
|
|
|
58
197
|
if [ "$IS_NPX_MODE" -eq 1 ]; then
|
|
59
198
|
title "选择安装目录(npx 模式)"
|
|
60
|
-
DEFAULT_TARGET_DIR="$
|
|
199
|
+
DEFAULT_TARGET_DIR="$INVOKE_PWD/silicaclaw"
|
|
61
200
|
TARGET_DIR_INPUT=""
|
|
62
201
|
read -r -p "请输入安装目录(默认: ${DEFAULT_TARGET_DIR:-$HOME/silicaclaw}): " TARGET_DIR_INPUT || true
|
|
63
202
|
TARGET_DIR="${TARGET_DIR_INPUT:-$DEFAULT_TARGET_DIR}"
|
|
@@ -117,6 +256,68 @@ else
|
|
|
117
256
|
fi
|
|
118
257
|
fi
|
|
119
258
|
|
|
259
|
+
title "安装系统命令(silicaclaw)"
|
|
260
|
+
if command -v silicaclaw >/dev/null 2>&1; then
|
|
261
|
+
echo "已检测到系统命令: $(command -v silicaclaw)"
|
|
262
|
+
echo "跳过命令安装。"
|
|
263
|
+
else
|
|
264
|
+
echo "将尝试无 sudo 安装 silicaclaw 命令到 PATH 中可写目录。"
|
|
265
|
+
BIN_DIR="$(first_writable_path_dir || true)"
|
|
266
|
+
INSTALLED=0
|
|
267
|
+
if [ -n "${BIN_DIR:-}" ]; then
|
|
268
|
+
if install_command_shim "$BIN_DIR"; then
|
|
269
|
+
echo "命令已安装: $BIN_DIR/silicaclaw"
|
|
270
|
+
hash -r || true
|
|
271
|
+
if command -v silicaclaw >/dev/null 2>&1; then
|
|
272
|
+
echo "验证成功: $(command -v silicaclaw)"
|
|
273
|
+
INSTALLED=1
|
|
274
|
+
else
|
|
275
|
+
echo "命令已写入,但当前 shell 未立即识别。请新开终端后运行 silicaclaw。"
|
|
276
|
+
fi
|
|
277
|
+
else
|
|
278
|
+
echo "命令安装失败。可继续使用: npx @silicaclaw/cli@beta <command>"
|
|
279
|
+
fi
|
|
280
|
+
else
|
|
281
|
+
echo "当前 PATH 中没有可写目录,无法无 sudo 安装系统命令。"
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
if [ "$INSTALLED" != "1" ]; then
|
|
285
|
+
SYS_BIN_DIR="$(default_system_bin_dir)"
|
|
286
|
+
echo "为保证开箱即用体验,建议安装到系统目录: $SYS_BIN_DIR/silicaclaw"
|
|
287
|
+
if ask_yes_no "是否使用 sudo 安装系统命令?" "Y"; then
|
|
288
|
+
run_cmd "sudo mkdir -p \"$SYS_BIN_DIR\""
|
|
289
|
+
run_cmd "sudo tee \"$SYS_BIN_DIR/silicaclaw\" >/dev/null <<'EOF'
|
|
290
|
+
#!/usr/bin/env bash
|
|
291
|
+
set -euo pipefail
|
|
292
|
+
exec node \"$WORK_DIR/scripts/silicaclaw-cli.mjs\" \"\$@\"
|
|
293
|
+
EOF"
|
|
294
|
+
run_cmd "sudo chmod +x \"$SYS_BIN_DIR/silicaclaw\""
|
|
295
|
+
hash -r || true
|
|
296
|
+
if command -v silicaclaw >/dev/null 2>&1; then
|
|
297
|
+
echo "验证成功: $(command -v silicaclaw)"
|
|
298
|
+
INSTALLED=1
|
|
299
|
+
else
|
|
300
|
+
echo "安装完成,但当前 shell 未刷新。请新开终端后运行 silicaclaw。"
|
|
301
|
+
fi
|
|
302
|
+
else
|
|
303
|
+
echo "已跳过 sudo 安装。"
|
|
304
|
+
fi
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
if [ "$INSTALLED" != "1" ]; then
|
|
308
|
+
echo "无需改 PATH/环境变量,也可一键使用 silicaclaw。"
|
|
309
|
+
if ask_yes_no "是否自动写入 shell alias(silicaclaw -> npx @silicaclaw/cli@beta)?" "Y"; then
|
|
310
|
+
if install_npx_alias; then
|
|
311
|
+
echo "alias 安装完成。新开终端后可直接使用 silicaclaw。"
|
|
312
|
+
else
|
|
313
|
+
echo "alias 安装失败。可继续使用: npx @silicaclaw/cli@beta <command>"
|
|
314
|
+
fi
|
|
315
|
+
else
|
|
316
|
+
echo "你仍可继续使用: npx @silicaclaw/cli@beta <command>"
|
|
317
|
+
fi
|
|
318
|
+
fi
|
|
319
|
+
fi
|
|
320
|
+
|
|
120
321
|
title "准备 social.md"
|
|
121
322
|
if [ -f "$WORK_DIR/social.md" ]; then
|
|
122
323
|
echo "已存在 social.md,保留现有配置。"
|
|
@@ -132,14 +333,22 @@ fi
|
|
|
132
333
|
title "选择网络模式"
|
|
133
334
|
echo "1) local 单机预览(最快)"
|
|
134
335
|
echo "2) lan 局域网预览(A/B 双机)"
|
|
135
|
-
echo "3) global-preview
|
|
136
|
-
|
|
137
|
-
|
|
336
|
+
echo "3) global-preview 互联网预览(Relay,推荐)"
|
|
337
|
+
echo "提示: 不确定就直接回车(默认 global-preview)。"
|
|
338
|
+
echo "提示: 互联网场景需要一个所有节点都可访问的 relay/signaling 地址。"
|
|
339
|
+
if [ "$CONNECT_MODE" = "1" ]; then
|
|
340
|
+
MODE_PICK="3"
|
|
341
|
+
echo "connect 模式:已自动选择 global-preview。"
|
|
342
|
+
else
|
|
343
|
+
read -r -p "请输入模式编号 [1/2/3] (默认 ${DEFAULT_MODE_PICK}): " MODE_PICK || true
|
|
344
|
+
MODE_PICK="${MODE_PICK:-$DEFAULT_MODE_PICK}"
|
|
345
|
+
fi
|
|
138
346
|
|
|
139
347
|
NETWORK_MODE="local"
|
|
140
348
|
NETWORK_ADAPTER="local-event-bus"
|
|
141
349
|
WEBRTC_SIGNALING_URL_VALUE=""
|
|
142
|
-
WEBRTC_ROOM_VALUE="silicaclaw-
|
|
350
|
+
WEBRTC_ROOM_VALUE="silicaclaw-global-preview"
|
|
351
|
+
AUTO_START_SIGNALING=0
|
|
143
352
|
|
|
144
353
|
case "$MODE_PICK" in
|
|
145
354
|
2)
|
|
@@ -148,14 +357,40 @@ case "$MODE_PICK" in
|
|
|
148
357
|
;;
|
|
149
358
|
3)
|
|
150
359
|
NETWORK_MODE="global-preview"
|
|
151
|
-
NETWORK_ADAPTER="
|
|
152
|
-
|
|
360
|
+
NETWORK_ADAPTER="relay-preview"
|
|
361
|
+
PUBLIC_IP="$(detect_public_ip)"
|
|
362
|
+
SIGNALING_DEFAULT="${WEBRTC_SIGNALING_URL:-http://localhost:4510}"
|
|
363
|
+
if [ -n "$PUBLIC_IP" ]; then
|
|
364
|
+
SIGNALING_DEFAULT="http://$PUBLIC_IP:4510"
|
|
365
|
+
fi
|
|
366
|
+
echo "提示: signaling 地址需要“所有节点可访问”。"
|
|
367
|
+
if [ -n "$PUBLIC_IP" ]; then
|
|
368
|
+
echo "已检测到本机公网 IP: $PUBLIC_IP"
|
|
369
|
+
echo "如果你这台机器就是 signaling 服务器,可直接回车使用默认值。"
|
|
370
|
+
else
|
|
371
|
+
echo "未检测到公网 IP,将使用默认值: $SIGNALING_DEFAULT"
|
|
372
|
+
echo "如果 signaling 在其他机器,请输入该机器公网地址。"
|
|
373
|
+
fi
|
|
374
|
+
read -r -p "请输入 signaling URL(默认 ${SIGNALING_DEFAULT}): " WEBRTC_SIGNALING_URL_INPUT || true
|
|
375
|
+
WEBRTC_SIGNALING_URL_VALUE="${WEBRTC_SIGNALING_URL_INPUT:-$SIGNALING_DEFAULT}"
|
|
153
376
|
if [ -z "${WEBRTC_SIGNALING_URL_VALUE:-}" ]; then
|
|
154
|
-
echo "global-preview
|
|
377
|
+
echo "global-preview 必须提供公网可达的 signaling URL"
|
|
155
378
|
exit 1
|
|
156
379
|
fi
|
|
157
|
-
|
|
158
|
-
|
|
380
|
+
|
|
381
|
+
SIGNALING_HOST="$(url_host "$WEBRTC_SIGNALING_URL_VALUE")"
|
|
382
|
+
if [ "$SIGNALING_HOST" = "localhost" ] || [ "$SIGNALING_HOST" = "127.0.0.1" ]; then
|
|
383
|
+
echo "提示: 当前 signaling URL 是本机地址,仅本机可用,不适合跨网络双机演示。"
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
if ask_yes_no "是否在当前机器自动后台启动 signaling server(用于演示)?" "Y"; then
|
|
387
|
+
AUTO_START_SIGNALING=1
|
|
388
|
+
SIGNALING_PORT_VALUE="$(url_port_or_default "$WEBRTC_SIGNALING_URL_VALUE" "4510")"
|
|
389
|
+
echo "将尝试以 PORT=$SIGNALING_PORT_VALUE 后台启动 signaling server"
|
|
390
|
+
fi
|
|
391
|
+
|
|
392
|
+
read -r -p "请输入 room(默认 silicaclaw-global-preview): " WEBRTC_ROOM_VALUE_INPUT || true
|
|
393
|
+
WEBRTC_ROOM_VALUE="${WEBRTC_ROOM_VALUE_INPUT:-silicaclaw-global-preview}"
|
|
159
394
|
;;
|
|
160
395
|
*)
|
|
161
396
|
NETWORK_MODE="local"
|
|
@@ -166,16 +401,47 @@ esac
|
|
|
166
401
|
echo "已选择: $NETWORK_MODE ($NETWORK_ADAPTER)"
|
|
167
402
|
|
|
168
403
|
title "启动 local-console"
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
404
|
+
echo "1) gateway(推荐) 后台服务模式,可用 start/stop/restart/status 管理"
|
|
405
|
+
echo "2) dev watch 前台开发模式(tsx watch)"
|
|
406
|
+
read -r -p "请选择启动方式 [1/2] (默认 1): " START_MODE_PICK || true
|
|
407
|
+
START_MODE_PICK="${START_MODE_PICK:-1}"
|
|
408
|
+
|
|
409
|
+
if [ "$START_MODE_PICK" = "2" ]; then
|
|
410
|
+
if [ "$NETWORK_MODE" = "global-preview" ]; then
|
|
411
|
+
if [ "$AUTO_START_SIGNALING" -eq 1 ]; then
|
|
412
|
+
mkdir -p "$WORK_DIR/.silicaclaw"
|
|
413
|
+
SIGNALING_LOG="$WORK_DIR/.silicaclaw/signaling.log"
|
|
414
|
+
SIGNALING_PID_FILE="$WORK_DIR/.silicaclaw/signaling.pid"
|
|
415
|
+
run_cmd "cd \"$WORK_DIR\" && PORT=${SIGNALING_PORT_VALUE:-4510} nohup npm run webrtc-signaling > \"$SIGNALING_LOG\" 2>&1 & echo \$! > \"$SIGNALING_PID_FILE\""
|
|
416
|
+
echo "已后台启动 signaling server,日志: $SIGNALING_LOG"
|
|
417
|
+
echo "停止 signaling: kill \$(cat \"$SIGNALING_PID_FILE\")"
|
|
418
|
+
fi
|
|
419
|
+
echo "将使用以下参数启动(dev watch):"
|
|
420
|
+
echo "NETWORK_ADAPTER=$NETWORK_ADAPTER"
|
|
421
|
+
echo "WEBRTC_SIGNALING_URL=$WEBRTC_SIGNALING_URL_VALUE"
|
|
422
|
+
echo "WEBRTC_ROOM=$WEBRTC_ROOM_VALUE"
|
|
423
|
+
pause_continue
|
|
424
|
+
run_cmd "cd \"$WORK_DIR\" && NETWORK_ADAPTER=$NETWORK_ADAPTER WEBRTC_SIGNALING_URL=$WEBRTC_SIGNALING_URL_VALUE WEBRTC_ROOM=$WEBRTC_ROOM_VALUE npm run local-console"
|
|
425
|
+
else
|
|
426
|
+
echo "将使用以下参数启动(dev watch):"
|
|
427
|
+
echo "NETWORK_ADAPTER=$NETWORK_ADAPTER"
|
|
428
|
+
pause_continue
|
|
429
|
+
run_cmd "cd \"$WORK_DIR\" && NETWORK_ADAPTER=$NETWORK_ADAPTER npm run local-console"
|
|
430
|
+
fi
|
|
176
431
|
else
|
|
177
|
-
|
|
178
|
-
|
|
432
|
+
GATEWAY_CMD="cd \"$WORK_DIR\" && npm run gateway -- start --mode=$NETWORK_MODE"
|
|
433
|
+
if [ "$NETWORK_MODE" = "global-preview" ]; then
|
|
434
|
+
GATEWAY_CMD="$GATEWAY_CMD --signaling-url=$WEBRTC_SIGNALING_URL_VALUE --room=$WEBRTC_ROOM_VALUE"
|
|
435
|
+
fi
|
|
436
|
+
echo "将使用 gateway 后台启动:"
|
|
437
|
+
echo "$GATEWAY_CMD"
|
|
179
438
|
pause_continue
|
|
180
|
-
run_cmd "
|
|
439
|
+
run_cmd "$GATEWAY_CMD"
|
|
440
|
+
echo ""
|
|
441
|
+
echo "已启动完成。常用命令:"
|
|
442
|
+
echo "cd \"$WORK_DIR\" && npm run gateway -- status"
|
|
443
|
+
echo "cd \"$WORK_DIR\" && npm run gateway -- logs local-console"
|
|
444
|
+
echo "cd \"$WORK_DIR\" && npm run gateway -- stop"
|
|
445
|
+
echo ""
|
|
446
|
+
echo "打开: http://localhost:4310"
|
|
181
447
|
fi
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { accessSync, constants, cpSync, existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { dirname, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
@@ -23,18 +23,254 @@ function run(cmd, args, extra = {}) {
|
|
|
23
23
|
process.exit(result.status ?? 0);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function runCapture(cmd, args, extra = {}) {
|
|
27
|
+
const result = spawnSync(cmd, args, {
|
|
28
|
+
cwd: ROOT_DIR,
|
|
29
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
30
|
+
encoding: "utf8",
|
|
31
|
+
env: process.env,
|
|
32
|
+
...extra,
|
|
33
|
+
});
|
|
34
|
+
if (result.error) {
|
|
35
|
+
throw result.error;
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function runInherit(cmd, args, extra = {}) {
|
|
41
|
+
const result = spawnSync(cmd, args, {
|
|
42
|
+
cwd: ROOT_DIR,
|
|
43
|
+
stdio: "inherit",
|
|
44
|
+
env: process.env,
|
|
45
|
+
...extra,
|
|
46
|
+
});
|
|
47
|
+
if (result.error) {
|
|
48
|
+
throw result.error;
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
function readVersion() {
|
|
27
54
|
const versionFile = resolve(ROOT_DIR, "VERSION");
|
|
28
55
|
if (!existsSync(versionFile)) return "unknown";
|
|
29
56
|
return readFileSync(versionFile, "utf8").trim() || "unknown";
|
|
30
57
|
}
|
|
31
58
|
|
|
59
|
+
function readPackageVersion() {
|
|
60
|
+
const pkgFile = resolve(ROOT_DIR, "package.json");
|
|
61
|
+
if (!existsSync(pkgFile)) return "unknown";
|
|
62
|
+
try {
|
|
63
|
+
const pkg = JSON.parse(readFileSync(pkgFile, "utf8"));
|
|
64
|
+
return String(pkg.version || "unknown");
|
|
65
|
+
} catch {
|
|
66
|
+
return "unknown";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isNpxRun() {
|
|
71
|
+
return ROOT_DIR.includes("/.npm/_npx/");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function canWriteGlobalPrefix() {
|
|
75
|
+
try {
|
|
76
|
+
const prefixResult = runCapture("npm", ["prefix", "-g"]);
|
|
77
|
+
if ((prefixResult.status ?? 1) !== 0) return false;
|
|
78
|
+
const prefix = String(prefixResult.stdout || "").trim();
|
|
79
|
+
if (!prefix) return false;
|
|
80
|
+
const targetDir = resolve(prefix, "lib", "node_modules");
|
|
81
|
+
accessSync(targetDir, constants.W_OK);
|
|
82
|
+
return true;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function showUpdateGuide(current, latest, beta) {
|
|
89
|
+
const npxRuntime = isNpxRun();
|
|
90
|
+
console.log("SilicaClaw update check");
|
|
91
|
+
console.log(`current: ${current}`);
|
|
92
|
+
console.log(`latest : ${latest || "-"}`);
|
|
93
|
+
console.log(`beta : ${beta || "-"}`);
|
|
94
|
+
console.log("");
|
|
95
|
+
|
|
96
|
+
const upToDate = Boolean(beta) && current === beta;
|
|
97
|
+
if (upToDate) {
|
|
98
|
+
console.log("You are already on the latest beta.");
|
|
99
|
+
} else {
|
|
100
|
+
console.log("Update available.");
|
|
101
|
+
}
|
|
102
|
+
console.log("");
|
|
103
|
+
console.log("Quick next commands:");
|
|
104
|
+
console.log("1) Start internet gateway");
|
|
105
|
+
console.log(" silicaclaw start --mode=global-preview --signaling-url=https://your-relay.example");
|
|
106
|
+
console.log("2) Check gateway status");
|
|
107
|
+
console.log(" silicaclaw status");
|
|
108
|
+
console.log("3) npx one-shot (no alias/global install)");
|
|
109
|
+
console.log(" npx -y @silicaclaw/cli@beta start --mode=global-preview --signaling-url=https://your-relay.example");
|
|
110
|
+
console.log("");
|
|
111
|
+
|
|
112
|
+
const writableGlobal = canWriteGlobalPrefix();
|
|
113
|
+
if (!npxRuntime && writableGlobal) {
|
|
114
|
+
console.log("Optional global install:");
|
|
115
|
+
console.log(" npm i -g @silicaclaw/cli@beta");
|
|
116
|
+
console.log(" silicaclaw version");
|
|
117
|
+
console.log("");
|
|
118
|
+
} else if (!npxRuntime) {
|
|
119
|
+
console.log("Global install skipped: npm global directory is not writable (likely EACCES).");
|
|
120
|
+
console.log("");
|
|
121
|
+
}
|
|
122
|
+
if (npxRuntime) {
|
|
123
|
+
console.log("Detected npx runtime.");
|
|
124
|
+
console.log("If `silicaclaw` is unavailable in this shell, use the npx one-shot command above.");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getGatewayStatus() {
|
|
129
|
+
try {
|
|
130
|
+
const result = runCapture("node", [resolve(ROOT_DIR, "scripts", "silicaclaw-gateway.mjs"), "status"], {
|
|
131
|
+
cwd: process.cwd(),
|
|
132
|
+
});
|
|
133
|
+
if ((result.status ?? 1) !== 0) return null;
|
|
134
|
+
const text = String(result.stdout || "").trim();
|
|
135
|
+
return text ? JSON.parse(text) : null;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function isManagedAppDir(appDir) {
|
|
142
|
+
if (!appDir || !existsSync(resolve(appDir, "package.json"))) return false;
|
|
143
|
+
try {
|
|
144
|
+
const pkg = JSON.parse(readFileSync(resolve(appDir, "package.json"), "utf8"));
|
|
145
|
+
const name = String(pkg?.name || "");
|
|
146
|
+
return name === "@silicaclaw/cli" || name === "silicaclaw";
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function syncCurrentPackageToAppDir(appDir) {
|
|
153
|
+
if (!appDir || resolve(appDir) === ROOT_DIR) return false;
|
|
154
|
+
if (!isManagedAppDir(appDir)) return false;
|
|
155
|
+
|
|
156
|
+
const entries = [
|
|
157
|
+
"apps/local-console",
|
|
158
|
+
"apps/public-explorer",
|
|
159
|
+
"packages/core",
|
|
160
|
+
"packages/network",
|
|
161
|
+
"packages/storage",
|
|
162
|
+
"scripts",
|
|
163
|
+
"README.md",
|
|
164
|
+
"INSTALL.md",
|
|
165
|
+
"CHANGELOG.md",
|
|
166
|
+
"ARCHITECTURE.md",
|
|
167
|
+
"ROADMAP.md",
|
|
168
|
+
"SOCIAL_MD_SPEC.md",
|
|
169
|
+
"DEMO_GUIDE.md",
|
|
170
|
+
"RELEASE_NOTES_v1.0.md",
|
|
171
|
+
"social.md.example",
|
|
172
|
+
"openclaw.social.md.example",
|
|
173
|
+
"VERSION",
|
|
174
|
+
"package.json",
|
|
175
|
+
"package-lock.json",
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
for (const rel of entries) {
|
|
179
|
+
const src = resolve(ROOT_DIR, rel);
|
|
180
|
+
if (!existsSync(src)) continue;
|
|
181
|
+
const dst = resolve(appDir, rel);
|
|
182
|
+
cpSync(src, dst, { recursive: true, force: true });
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function restartGatewayIfRunning() {
|
|
188
|
+
const status = getGatewayStatus();
|
|
189
|
+
const appDir = status?.app_dir ? String(status.app_dir) : "";
|
|
190
|
+
const synced = syncCurrentPackageToAppDir(appDir);
|
|
191
|
+
if (synced) {
|
|
192
|
+
console.log(`Synced runtime files to app_dir: ${appDir}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const localRunning = Boolean(status?.local_console?.running);
|
|
196
|
+
const signalingRunning = Boolean(status?.signaling?.running);
|
|
197
|
+
if (!localRunning && !signalingRunning) {
|
|
198
|
+
console.log("Gateway not running: no restart needed.");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const mode = String(status?.mode || "local");
|
|
203
|
+
const args = [resolve(ROOT_DIR, "scripts", "silicaclaw-gateway.mjs"), "restart", `--mode=${mode}`];
|
|
204
|
+
if (mode === "global-preview" && status?.signaling?.url) {
|
|
205
|
+
args.push(`--signaling-url=${status.signaling.url}`);
|
|
206
|
+
}
|
|
207
|
+
if (mode === "global-preview" && status?.signaling?.room) {
|
|
208
|
+
args.push(`--room=${status.signaling.room}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log("Refreshing gateway services...");
|
|
212
|
+
runInherit("node", args, { cwd: process.cwd() });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function tryGlobalUpgrade(beta) {
|
|
216
|
+
const writableGlobal = canWriteGlobalPrefix();
|
|
217
|
+
if (!writableGlobal) return false;
|
|
218
|
+
console.log(`Installing @silicaclaw/cli@${beta} globally...`);
|
|
219
|
+
const result = runInherit("npm", ["i", "-g", `@silicaclaw/cli@${beta}`]);
|
|
220
|
+
return (result.status ?? 1) === 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function update() {
|
|
224
|
+
const current = readPackageVersion();
|
|
225
|
+
try {
|
|
226
|
+
const result = runCapture("npm", ["view", "@silicaclaw/cli", "dist-tags", "--json"]);
|
|
227
|
+
if ((result.status ?? 1) !== 0) {
|
|
228
|
+
console.error("Failed to query npm dist-tags.");
|
|
229
|
+
if (result.stderr) console.error(result.stderr.trim());
|
|
230
|
+
process.exit(result.status ?? 1);
|
|
231
|
+
}
|
|
232
|
+
const text = String(result.stdout || "").trim();
|
|
233
|
+
const tags = text ? JSON.parse(text) : {};
|
|
234
|
+
const latest = tags.latest ? String(tags.latest) : "";
|
|
235
|
+
const beta = tags.beta ? String(tags.beta) : "";
|
|
236
|
+
showUpdateGuide(current, latest, beta);
|
|
237
|
+
const hasNewBeta = Boolean(beta) && beta !== current;
|
|
238
|
+
const npxRuntime = isNpxRun();
|
|
239
|
+
|
|
240
|
+
if (hasNewBeta) {
|
|
241
|
+
if (npxRuntime) {
|
|
242
|
+
console.log(`New beta detected (${beta}). npx will use latest on next run.`);
|
|
243
|
+
} else if (tryGlobalUpgrade(beta)) {
|
|
244
|
+
console.log(`Global upgrade completed: ${beta}`);
|
|
245
|
+
} else {
|
|
246
|
+
console.log("Skipped global upgrade (no write permission or upgrade failed).");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
restartGatewayIfRunning();
|
|
251
|
+
process.exit(0);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error(`Update check failed: ${error.message}`);
|
|
254
|
+
console.log("Try manually:");
|
|
255
|
+
console.log("npm view @silicaclaw/cli dist-tags --json");
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
32
260
|
function help() {
|
|
33
261
|
console.log(`
|
|
34
262
|
SilicaClaw CLI
|
|
35
263
|
|
|
36
264
|
Usage:
|
|
37
265
|
silicaclaw onboard
|
|
266
|
+
silicaclaw connect
|
|
267
|
+
silicaclaw update
|
|
268
|
+
silicaclaw start [--mode=local|lan|global-preview]
|
|
269
|
+
silicaclaw stop
|
|
270
|
+
silicaclaw restart [--mode=local|lan|global-preview]
|
|
271
|
+
silicaclaw status
|
|
272
|
+
silicaclaw logs [local-console|signaling]
|
|
273
|
+
silicaclaw gateway <start|stop|restart|status|logs>
|
|
38
274
|
silicaclaw local-console
|
|
39
275
|
silicaclaw explorer
|
|
40
276
|
silicaclaw signaling
|
|
@@ -44,6 +280,14 @@ Usage:
|
|
|
44
280
|
|
|
45
281
|
Commands:
|
|
46
282
|
onboard Interactive step-by-step onboarding (recommended)
|
|
283
|
+
connect Cross-network connect wizard (global-preview first)
|
|
284
|
+
update Check latest npm version and show upgrade commands
|
|
285
|
+
start Start gateway-managed background services
|
|
286
|
+
stop Stop gateway-managed background services
|
|
287
|
+
restart Restart gateway-managed background services
|
|
288
|
+
status Show gateway-managed service status
|
|
289
|
+
logs Show gateway-managed service logs
|
|
290
|
+
gateway Manage background services (start/stop/restart/status/logs)
|
|
47
291
|
local-console Start local console (http://localhost:4310)
|
|
48
292
|
explorer Start public explorer (http://localhost:4311)
|
|
49
293
|
signaling Start WebRTC signaling preview server
|
|
@@ -59,6 +303,32 @@ switch (cmd) {
|
|
|
59
303
|
case "onboard":
|
|
60
304
|
run("bash", [resolve(ROOT_DIR, "scripts", "quickstart.sh")]);
|
|
61
305
|
break;
|
|
306
|
+
case "connect":
|
|
307
|
+
run("bash", [resolve(ROOT_DIR, "scripts", "quickstart.sh")], {
|
|
308
|
+
env: {
|
|
309
|
+
...process.env,
|
|
310
|
+
QUICKSTART_DEFAULT_MODE: "3",
|
|
311
|
+
QUICKSTART_CONNECT_MODE: "1",
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
break;
|
|
315
|
+
case "update":
|
|
316
|
+
update();
|
|
317
|
+
break;
|
|
318
|
+
case "gateway":
|
|
319
|
+
run("node", [resolve(ROOT_DIR, "scripts", "silicaclaw-gateway.mjs"), ...process.argv.slice(3)], {
|
|
320
|
+
cwd: process.cwd(),
|
|
321
|
+
});
|
|
322
|
+
break;
|
|
323
|
+
case "start":
|
|
324
|
+
case "stop":
|
|
325
|
+
case "restart":
|
|
326
|
+
case "status":
|
|
327
|
+
case "logs":
|
|
328
|
+
run("node", [resolve(ROOT_DIR, "scripts", "silicaclaw-gateway.mjs"), cmd, ...process.argv.slice(3)], {
|
|
329
|
+
cwd: process.cwd(),
|
|
330
|
+
});
|
|
331
|
+
break;
|
|
62
332
|
case "local-console":
|
|
63
333
|
case "console":
|
|
64
334
|
run("npm", ["run", "local-console"]);
|