@silicaclaw/cli 2026.3.19-9 → 2026.3.20-2
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 +134 -0
- package/DEMO_GUIDE.md +1 -1
- package/INSTALL.md +47 -13
- package/README.md +54 -19
- package/VERSION +1 -1
- package/apps/local-console/dist/apps/local-console/src/server.d.ts +51 -12
- package/apps/local-console/dist/apps/local-console/src/server.js +619 -183
- package/apps/local-console/dist/config/silicaclaw-defaults.json +19 -0
- package/apps/local-console/dist/packages/core/src/socialConfig.js +9 -5
- package/apps/local-console/dist/packages/network/src/realPreview.js +6 -2
- package/apps/local-console/dist/packages/network/src/relayPreview.js +8 -2
- package/apps/local-console/dist/packages/network/src/transport/udpLanBroadcastTransport.js +2 -1
- package/apps/local-console/dist/packages/network/src/webrtcPreview.js +5 -1
- package/apps/local-console/dist/packages/storage/config/silicaclaw-defaults.json +19 -0
- package/apps/local-console/dist/packages/storage/src/socialRuntimeRepo.js +8 -4
- package/apps/local-console/public/app/app.js +21 -1
- package/apps/local-console/public/app/events.js +40 -2
- package/apps/local-console/public/app/network.js +32 -3
- package/apps/local-console/public/app/overview.js +18 -26
- package/apps/local-console/public/app/shell.js +18 -34
- package/apps/local-console/public/app/social.js +207 -28
- package/apps/local-console/public/app/styles.css +182 -14
- package/apps/local-console/public/app/template.js +76 -31
- package/apps/local-console/public/app/translations.js +147 -51
- package/apps/local-console/src/server.ts +652 -189
- package/apps/public-explorer/dist/apps/public-explorer/src/server.d.ts +1 -0
- package/apps/public-explorer/dist/apps/public-explorer/src/server.js +41 -0
- package/apps/public-explorer/dist/config/silicaclaw-defaults.json +19 -0
- package/apps/public-explorer/public/app/app.js +22 -2
- package/apps/public-explorer/public/app/template.js +4 -4
- package/apps/public-explorer/public/app/translations.js +15 -15
- package/apps/public-explorer/src/server.ts +11 -1
- package/config/silicaclaw-defaults.json +19 -0
- package/dist/apps/local-console/src/server.d.ts +1 -0
- package/dist/apps/local-console/src/server.js +555 -0
- package/docs/NEW_USER_INSTALL.md +14 -10
- package/docs/NEW_USER_OPERATIONS.md +4 -4
- package/docs/OPENCLAW_BRIDGE.md +15 -0
- package/docs/OPENCLAW_BRIDGE_ZH.md +15 -0
- package/docs/RELEASE_CHECKLIST.md +95 -0
- package/node_modules/@silicaclaw/core/dist/config/silicaclaw-defaults.json +19 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/crypto.d.ts +6 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/crypto.js +50 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/directory.d.ts +17 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/directory.js +145 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/identity.d.ts +2 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/identity.js +18 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/index.d.ts +12 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/index.js +28 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/indexing.d.ts +6 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/indexing.js +43 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/presence.d.ts +4 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/presence.js +23 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/profile.d.ts +4 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/profile.js +39 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.d.ts +70 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.js +103 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/socialConfig.d.ts +100 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/socialConfig.js +300 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/socialMessage.d.ts +19 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/socialMessage.js +69 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/socialResolver.d.ts +46 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/socialResolver.js +237 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/socialTemplate.d.ts +2 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/socialTemplate.js +90 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/types.d.ts +59 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/types.js +2 -0
- package/node_modules/@silicaclaw/core/src/socialConfig.ts +7 -5
- package/node_modules/@silicaclaw/network/dist/config/silicaclaw-defaults.json +19 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/messageEnvelope.d.ts +28 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/messageEnvelope.js +36 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/peerDiscovery.d.ts +43 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/peerDiscovery.js +2 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/topicCodec.d.ts +4 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/topicCodec.js +2 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/transport.d.ts +36 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/abstractions/transport.js +2 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/codec/jsonMessageEnvelopeCodec.d.ts +5 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/codec/jsonMessageEnvelopeCodec.js +24 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/codec/jsonTopicCodec.d.ts +5 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/codec/jsonTopicCodec.js +12 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/discovery/heartbeatPeerDiscovery.d.ts +28 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/discovery/heartbeatPeerDiscovery.js +144 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/index.d.ts +14 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/index.js +30 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/localEventBus.d.ts +9 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/localEventBus.js +47 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/mock.d.ts +8 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/mock.js +24 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/realPreview.d.ts +105 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/realPreview.js +331 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.d.ts +166 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.js +448 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/transport/udpLanBroadcastTransport.d.ts +23 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/transport/udpLanBroadcastTransport.js +154 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/types.d.ts +6 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/types.js +2 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/webrtcPreview.d.ts +163 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/webrtcPreview.js +848 -0
- package/node_modules/@silicaclaw/network/src/realPreview.ts +3 -2
- package/node_modules/@silicaclaw/network/src/relayPreview.ts +5 -2
- package/node_modules/@silicaclaw/network/src/transport/udpLanBroadcastTransport.ts +2 -1
- package/node_modules/@silicaclaw/network/src/webrtcPreview.ts +2 -1
- package/node_modules/@silicaclaw/storage/config/silicaclaw-defaults.json +19 -0
- package/node_modules/@silicaclaw/storage/dist/config/silicaclaw-defaults.json +19 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/crypto.d.ts +6 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/crypto.js +50 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/directory.d.ts +17 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/directory.js +145 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/identity.d.ts +2 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/identity.js +18 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.d.ts +12 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.js +28 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/indexing.d.ts +6 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/indexing.js +43 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/presence.d.ts +4 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/presence.js +23 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/profile.d.ts +4 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/profile.js +39 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.d.ts +70 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.js +103 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialConfig.d.ts +100 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialConfig.js +300 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialMessage.d.ts +19 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialMessage.js +69 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialResolver.d.ts +46 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialResolver.js +237 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialTemplate.d.ts +2 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/socialTemplate.js +90 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/types.d.ts +59 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/types.js +2 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/config/silicaclaw-defaults.json +19 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/index.d.ts +3 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/index.js +19 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/jsonRepo.d.ts +7 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/jsonRepo.js +29 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.d.ts +61 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.js +67 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/socialRuntimeRepo.d.ts +5 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/socialRuntimeRepo.js +57 -0
- package/node_modules/@silicaclaw/storage/dist/socialRuntimeRepo.js +8 -4
- package/node_modules/@silicaclaw/storage/src/socialRuntimeRepo.ts +5 -4
- package/node_modules/@silicaclaw/storage/tsconfig.json +1 -6
- package/openclaw-skills/silicaclaw-bridge-setup/SKILL.md +147 -0
- package/openclaw-skills/silicaclaw-bridge-setup/VERSION +1 -0
- package/openclaw-skills/silicaclaw-bridge-setup/agents/openai.yaml +6 -0
- package/openclaw-skills/silicaclaw-bridge-setup/manifest.json +27 -0
- package/openclaw-skills/silicaclaw-bridge-setup/references/owner-dialogue-cheatsheet-zh.md +58 -0
- package/openclaw-skills/silicaclaw-bridge-setup/references/runtime-setup.md +43 -0
- package/openclaw-skills/silicaclaw-bridge-setup/references/troubleshooting.md +24 -0
- package/openclaw-skills/silicaclaw-broadcast/SKILL.md +132 -0
- package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -1
- package/openclaw-skills/silicaclaw-broadcast/agents/openai.yaml +2 -2
- package/openclaw-skills/silicaclaw-broadcast/manifest.json +3 -2
- package/openclaw-skills/silicaclaw-broadcast/references/owner-dialogue-cheatsheet-zh.md +81 -0
- package/openclaw-skills/silicaclaw-owner-push/SKILL.md +217 -0
- package/openclaw-skills/silicaclaw-owner-push/VERSION +1 -0
- package/openclaw-skills/silicaclaw-owner-push/agents/openai.yaml +6 -0
- package/openclaw-skills/silicaclaw-owner-push/manifest.json +30 -0
- package/openclaw-skills/silicaclaw-owner-push/references/owner-dialogue-cheatsheet-zh.md +87 -0
- package/openclaw-skills/silicaclaw-owner-push/references/push-routing-policy.md +43 -0
- package/openclaw-skills/silicaclaw-owner-push/references/runtime-setup.md +41 -0
- package/openclaw-skills/silicaclaw-owner-push/scripts/owner-push-forwarder.mjs +214 -0
- package/openclaw-skills/silicaclaw-owner-push/scripts/send-to-owner-via-openclaw.mjs +69 -0
- package/package.json +5 -1
- package/packages/core/dist/config/silicaclaw-defaults.json +19 -0
- package/packages/core/dist/packages/core/src/crypto.d.ts +6 -0
- package/packages/core/dist/packages/core/src/crypto.js +50 -0
- package/packages/core/dist/packages/core/src/directory.d.ts +17 -0
- package/packages/core/dist/packages/core/src/directory.js +145 -0
- package/packages/core/dist/packages/core/src/identity.d.ts +2 -0
- package/packages/core/dist/packages/core/src/identity.js +18 -0
- package/packages/core/dist/packages/core/src/index.d.ts +12 -0
- package/packages/core/dist/packages/core/src/index.js +28 -0
- package/packages/core/dist/packages/core/src/indexing.d.ts +6 -0
- package/packages/core/dist/packages/core/src/indexing.js +43 -0
- package/packages/core/dist/packages/core/src/presence.d.ts +4 -0
- package/packages/core/dist/packages/core/src/presence.js +23 -0
- package/packages/core/dist/packages/core/src/profile.d.ts +4 -0
- package/packages/core/dist/packages/core/src/profile.js +39 -0
- package/packages/core/dist/packages/core/src/publicProfileSummary.d.ts +70 -0
- package/packages/core/dist/packages/core/src/publicProfileSummary.js +103 -0
- package/packages/core/dist/packages/core/src/socialConfig.d.ts +100 -0
- package/packages/core/dist/packages/core/src/socialConfig.js +300 -0
- package/packages/core/dist/packages/core/src/socialMessage.d.ts +19 -0
- package/packages/core/dist/packages/core/src/socialMessage.js +69 -0
- package/packages/core/dist/packages/core/src/socialResolver.d.ts +46 -0
- package/packages/core/dist/packages/core/src/socialResolver.js +237 -0
- package/packages/core/dist/packages/core/src/socialTemplate.d.ts +2 -0
- package/packages/core/dist/packages/core/src/socialTemplate.js +90 -0
- package/packages/core/dist/packages/core/src/types.d.ts +59 -0
- package/packages/core/dist/packages/core/src/types.js +2 -0
- package/packages/core/src/socialConfig.ts +7 -5
- package/packages/network/dist/config/silicaclaw-defaults.json +19 -0
- package/packages/network/dist/packages/network/src/abstractions/messageEnvelope.d.ts +28 -0
- package/packages/network/dist/packages/network/src/abstractions/messageEnvelope.js +36 -0
- package/packages/network/dist/packages/network/src/abstractions/peerDiscovery.d.ts +43 -0
- package/packages/network/dist/packages/network/src/abstractions/peerDiscovery.js +2 -0
- package/packages/network/dist/packages/network/src/abstractions/topicCodec.d.ts +4 -0
- package/packages/network/dist/packages/network/src/abstractions/topicCodec.js +2 -0
- package/packages/network/dist/packages/network/src/abstractions/transport.d.ts +36 -0
- package/packages/network/dist/packages/network/src/abstractions/transport.js +2 -0
- package/packages/network/dist/packages/network/src/codec/jsonMessageEnvelopeCodec.d.ts +5 -0
- package/packages/network/dist/packages/network/src/codec/jsonMessageEnvelopeCodec.js +24 -0
- package/packages/network/dist/packages/network/src/codec/jsonTopicCodec.d.ts +5 -0
- package/packages/network/dist/packages/network/src/codec/jsonTopicCodec.js +12 -0
- package/packages/network/dist/packages/network/src/discovery/heartbeatPeerDiscovery.d.ts +28 -0
- package/packages/network/dist/packages/network/src/discovery/heartbeatPeerDiscovery.js +144 -0
- package/packages/network/dist/packages/network/src/index.d.ts +14 -0
- package/packages/network/dist/packages/network/src/index.js +30 -0
- package/packages/network/dist/packages/network/src/localEventBus.d.ts +9 -0
- package/packages/network/dist/packages/network/src/localEventBus.js +47 -0
- package/packages/network/dist/packages/network/src/mock.d.ts +8 -0
- package/packages/network/dist/packages/network/src/mock.js +24 -0
- package/packages/network/dist/packages/network/src/realPreview.d.ts +105 -0
- package/packages/network/dist/packages/network/src/realPreview.js +331 -0
- package/packages/network/dist/packages/network/src/relayPreview.d.ts +166 -0
- package/packages/network/dist/packages/network/src/relayPreview.js +448 -0
- package/packages/network/dist/packages/network/src/transport/udpLanBroadcastTransport.d.ts +23 -0
- package/packages/network/dist/packages/network/src/transport/udpLanBroadcastTransport.js +154 -0
- package/packages/network/dist/packages/network/src/types.d.ts +6 -0
- package/packages/network/dist/packages/network/src/types.js +2 -0
- package/packages/network/dist/packages/network/src/webrtcPreview.d.ts +163 -0
- package/packages/network/dist/packages/network/src/webrtcPreview.js +848 -0
- package/packages/network/src/realPreview.ts +3 -2
- package/packages/network/src/relayPreview.ts +5 -2
- package/packages/network/src/transport/udpLanBroadcastTransport.ts +2 -1
- package/packages/network/src/webrtcPreview.ts +2 -1
- package/packages/storage/config/silicaclaw-defaults.json +19 -0
- package/packages/storage/dist/config/silicaclaw-defaults.json +19 -0
- package/packages/storage/dist/packages/core/src/crypto.d.ts +6 -0
- package/packages/storage/dist/packages/core/src/crypto.js +50 -0
- package/packages/storage/dist/packages/core/src/directory.d.ts +17 -0
- package/packages/storage/dist/packages/core/src/directory.js +145 -0
- package/packages/storage/dist/packages/core/src/identity.d.ts +2 -0
- package/packages/storage/dist/packages/core/src/identity.js +18 -0
- package/packages/storage/dist/packages/core/src/index.d.ts +12 -0
- package/packages/storage/dist/packages/core/src/index.js +28 -0
- package/packages/storage/dist/packages/core/src/indexing.d.ts +6 -0
- package/packages/storage/dist/packages/core/src/indexing.js +43 -0
- package/packages/storage/dist/packages/core/src/presence.d.ts +4 -0
- package/packages/storage/dist/packages/core/src/presence.js +23 -0
- package/packages/storage/dist/packages/core/src/profile.d.ts +4 -0
- package/packages/storage/dist/packages/core/src/profile.js +39 -0
- package/packages/storage/dist/packages/core/src/publicProfileSummary.d.ts +70 -0
- package/packages/storage/dist/packages/core/src/publicProfileSummary.js +103 -0
- package/packages/storage/dist/packages/core/src/socialConfig.d.ts +100 -0
- package/packages/storage/dist/packages/core/src/socialConfig.js +300 -0
- package/packages/storage/dist/packages/core/src/socialMessage.d.ts +19 -0
- package/packages/storage/dist/packages/core/src/socialMessage.js +69 -0
- package/packages/storage/dist/packages/core/src/socialResolver.d.ts +46 -0
- package/packages/storage/dist/packages/core/src/socialResolver.js +237 -0
- package/packages/storage/dist/packages/core/src/socialTemplate.d.ts +2 -0
- package/packages/storage/dist/packages/core/src/socialTemplate.js +90 -0
- package/packages/storage/dist/packages/core/src/types.d.ts +59 -0
- package/packages/storage/dist/packages/core/src/types.js +2 -0
- package/packages/storage/dist/packages/storage/config/silicaclaw-defaults.json +19 -0
- package/packages/storage/dist/packages/storage/src/index.d.ts +3 -0
- package/packages/storage/dist/packages/storage/src/index.js +19 -0
- package/packages/storage/dist/packages/storage/src/jsonRepo.d.ts +7 -0
- package/packages/storage/dist/packages/storage/src/jsonRepo.js +29 -0
- package/packages/storage/dist/packages/storage/src/repos.d.ts +61 -0
- package/packages/storage/dist/packages/storage/src/repos.js +67 -0
- package/packages/storage/dist/packages/storage/src/socialRuntimeRepo.d.ts +5 -0
- package/packages/storage/dist/packages/storage/src/socialRuntimeRepo.js +57 -0
- package/packages/storage/dist/socialRuntimeRepo.js +8 -4
- package/packages/storage/src/socialRuntimeRepo.ts +5 -4
- package/packages/storage/tsconfig.json +1 -6
- package/scripts/functional-check.mjs +35 -6
- package/scripts/install-openclaw-skill.mjs +9 -2
- package/scripts/openclaw-bridge-adapter.mjs +3 -1
- package/scripts/openclaw-bridge-client.mjs +3 -1
- package/scripts/openclaw-runtime-demo.mjs +3 -1
- package/scripts/quickstart.sh +14 -10
- package/scripts/release-pack.mjs +59 -1
- package/scripts/silicaclaw-cli.mjs +162 -50
- package/scripts/silicaclaw-gateway.mjs +302 -84
- package/scripts/validate-openclaw-skill.mjs +79 -21
|
@@ -4,8 +4,9 @@ import { execFile, spawnSync } from "child_process";
|
|
|
4
4
|
import { resolve } from "path";
|
|
5
5
|
import { accessSync, constants, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "fs";
|
|
6
6
|
import { createHash } from "crypto";
|
|
7
|
-
import { hostname } from "os";
|
|
7
|
+
import { homedir, hostname } from "os";
|
|
8
8
|
import { promisify } from "util";
|
|
9
|
+
import defaults from "../../../config/silicaclaw-defaults.json";
|
|
9
10
|
import {
|
|
10
11
|
AgentIdentity,
|
|
11
12
|
DirectoryState,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
ingestPresenceRecord,
|
|
28
29
|
ingestProfileRecord,
|
|
29
30
|
isAgentOnline,
|
|
31
|
+
rebuildIndexForProfile,
|
|
30
32
|
loadSocialConfig,
|
|
31
33
|
getSocialConfigSearchPaths,
|
|
32
34
|
resolveIdentityWithSocial,
|
|
@@ -79,16 +81,22 @@ const NETWORK_MAX_PAST_DRIFT_MS = Number(process.env.NETWORK_MAX_PAST_DRIFT_MS |
|
|
|
79
81
|
const NETWORK_HEARTBEAT_INTERVAL_MS = Number(process.env.NETWORK_HEARTBEAT_INTERVAL_MS || 12_000);
|
|
80
82
|
const NETWORK_PEER_STALE_AFTER_MS = Number(process.env.NETWORK_PEER_STALE_AFTER_MS || 45_000);
|
|
81
83
|
const OPENCLAW_GATEWAY_HOST = "127.0.0.1";
|
|
82
|
-
const
|
|
84
|
+
const DEFAULT_NETWORK_MODE = defaults.network.default_mode as "global-preview";
|
|
85
|
+
const DEFAULT_NETWORK_NAMESPACE = defaults.network.default_namespace;
|
|
86
|
+
const DEFAULT_NETWORK_PORT = defaults.ports.network_default;
|
|
87
|
+
const DEFAULT_GLOBAL_SIGNALING_URL = defaults.network.global_preview.relay_url;
|
|
88
|
+
const DEFAULT_GLOBAL_ROOM = defaults.network.global_preview.room;
|
|
89
|
+
const DEFAULT_BRIDGE_API_BASE = defaults.bridge.api_base;
|
|
90
|
+
const OPENCLAW_GATEWAY_PORT = defaults.ports.openclaw_gateway;
|
|
83
91
|
const OPENCLAW_GATEWAY_URL = `http://${OPENCLAW_GATEWAY_HOST}:${OPENCLAW_GATEWAY_PORT}/`;
|
|
84
92
|
const NETWORK_PEER_REMOVE_AFTER_MS = Number(process.env.NETWORK_PEER_REMOVE_AFTER_MS || 180_000);
|
|
85
93
|
const NETWORK_UDP_BIND_ADDRESS = process.env.NETWORK_UDP_BIND_ADDRESS || "0.0.0.0";
|
|
86
94
|
const NETWORK_UDP_BROADCAST_ADDRESS = process.env.NETWORK_UDP_BROADCAST_ADDRESS || "255.255.255.255";
|
|
87
95
|
const NETWORK_PEER_ID = process.env.NETWORK_PEER_ID;
|
|
88
96
|
const NETWORK_MODE = process.env.NETWORK_MODE || "";
|
|
89
|
-
const WEBRTC_SIGNALING_URL = process.env.WEBRTC_SIGNALING_URL ||
|
|
97
|
+
const WEBRTC_SIGNALING_URL = process.env.WEBRTC_SIGNALING_URL || DEFAULT_GLOBAL_SIGNALING_URL;
|
|
90
98
|
const WEBRTC_SIGNALING_URLS = process.env.WEBRTC_SIGNALING_URLS || "";
|
|
91
|
-
const WEBRTC_ROOM = process.env.WEBRTC_ROOM ||
|
|
99
|
+
const WEBRTC_ROOM = process.env.WEBRTC_ROOM || DEFAULT_GLOBAL_ROOM;
|
|
92
100
|
const WEBRTC_SEED_PEERS = process.env.WEBRTC_SEED_PEERS || "";
|
|
93
101
|
const WEBRTC_BOOTSTRAP_HINTS = process.env.WEBRTC_BOOTSTRAP_HINTS || "";
|
|
94
102
|
const PROFILE_VERSION = "v0.9";
|
|
@@ -105,6 +113,8 @@ const SOCIAL_MESSAGE_DUPLICATE_WINDOW_MS = Number(process.env.SOCIAL_MESSAGE_DUP
|
|
|
105
113
|
const SOCIAL_MESSAGE_MAX_FUTURE_MS = Number(process.env.SOCIAL_MESSAGE_MAX_FUTURE_MS || 30_000);
|
|
106
114
|
const SOCIAL_MESSAGE_MAX_AGE_MS = Number(process.env.SOCIAL_MESSAGE_MAX_AGE_MS || 15 * 60_000);
|
|
107
115
|
const SOCIAL_MESSAGE_OBSERVATION_HISTORY_LIMIT = Number(process.env.SOCIAL_MESSAGE_OBSERVATION_HISTORY_LIMIT || 500);
|
|
116
|
+
const SOCIAL_MESSAGE_REPLAY_WINDOW_MS = Number(process.env.SOCIAL_MESSAGE_REPLAY_WINDOW_MS || 10 * 60_000);
|
|
117
|
+
const SOCIAL_MESSAGE_REPLAY_MAX_PER_BROADCAST = Number(process.env.SOCIAL_MESSAGE_REPLAY_MAX_PER_BROADCAST || 3);
|
|
108
118
|
const SOCIAL_MESSAGE_BLOCKED_AGENT_IDS = new Set(
|
|
109
119
|
dedupeStrings(parseListEnv(process.env.SOCIAL_MESSAGE_BLOCKED_AGENT_IDS || ""))
|
|
110
120
|
);
|
|
@@ -115,23 +125,67 @@ const execFileAsync = promisify(execFile);
|
|
|
115
125
|
const OPENCLAW_SKILL_NAME = "silicaclaw-broadcast";
|
|
116
126
|
|
|
117
127
|
function readWorkspaceVersion(workspaceRoot: string): string {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
128
|
+
const candidates = [
|
|
129
|
+
workspaceRoot,
|
|
130
|
+
process.cwd(),
|
|
131
|
+
resolve(__dirname, "..", "..", ".."),
|
|
132
|
+
resolve(__dirname, "..", "..", "..", ".."),
|
|
133
|
+
].filter((dir, index, list) => dir && list.indexOf(dir) === index);
|
|
134
|
+
for (const candidate of candidates) {
|
|
135
|
+
const pkgFile = resolve(candidate, "package.json");
|
|
136
|
+
if (existsSync(pkgFile)) {
|
|
137
|
+
try {
|
|
138
|
+
const pkg = JSON.parse(readFileSync(pkgFile, "utf8")) as { version?: string; name?: string };
|
|
139
|
+
if (pkg.version && (pkg.name === "@silicaclaw/cli" || existsSync(resolve(candidate, "apps", "local-console")))) {
|
|
140
|
+
return String(pkg.version);
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
// ignore
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const versionFile = resolve(candidate, "VERSION");
|
|
147
|
+
if (existsSync(versionFile)) {
|
|
148
|
+
const raw = readFileSync(versionFile, "utf8").trim();
|
|
149
|
+
if (raw) return raw;
|
|
125
150
|
}
|
|
126
|
-
}
|
|
127
|
-
const versionFile = resolve(workspaceRoot, "VERSION");
|
|
128
|
-
if (existsSync(versionFile)) {
|
|
129
|
-
const raw = readFileSync(versionFile, "utf8").trim();
|
|
130
|
-
if (raw) return raw;
|
|
131
151
|
}
|
|
132
152
|
return "unknown";
|
|
133
153
|
}
|
|
134
154
|
|
|
155
|
+
function normalizeVersionText(value: unknown): string {
|
|
156
|
+
const text = String(value || "").trim();
|
|
157
|
+
return text.startsWith("v") ? text.slice(1) : text;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function tokenizeVersion(value: unknown): Array<number | string> {
|
|
161
|
+
return normalizeVersionText(value)
|
|
162
|
+
.split(/[^0-9A-Za-z]+/)
|
|
163
|
+
.map((token) => token.trim())
|
|
164
|
+
.filter(Boolean)
|
|
165
|
+
.map((token) => (/^\d+$/.test(token) ? Number(token) : token.toLowerCase()));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function compareVersionTokens(left: unknown, right: unknown): number {
|
|
169
|
+
const leftTokens = tokenizeVersion(left);
|
|
170
|
+
const rightTokens = tokenizeVersion(right);
|
|
171
|
+
const maxLength = Math.max(leftTokens.length, rightTokens.length);
|
|
172
|
+
for (let index = 0; index < maxLength; index += 1) {
|
|
173
|
+
const leftToken = leftTokens[index];
|
|
174
|
+
const rightToken = rightTokens[index];
|
|
175
|
+
if (leftToken === undefined && rightToken === undefined) return 0;
|
|
176
|
+
if (leftToken === undefined) return -1;
|
|
177
|
+
if (rightToken === undefined) return 1;
|
|
178
|
+
if (typeof leftToken === "number" && typeof rightToken === "number") {
|
|
179
|
+
if (leftToken !== rightToken) return leftToken > rightToken ? 1 : -1;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const leftText = String(leftToken);
|
|
183
|
+
const rightText = String(rightToken);
|
|
184
|
+
if (leftText !== rightText) return leftText.localeCompare(rightText);
|
|
185
|
+
}
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
135
189
|
function resolveWorkspaceRoot(cwd = process.cwd()): string {
|
|
136
190
|
if (existsSync(resolve(cwd, "apps", "local-console", "package.json"))) {
|
|
137
191
|
return cwd;
|
|
@@ -143,7 +197,36 @@ function resolveWorkspaceRoot(cwd = process.cwd()): string {
|
|
|
143
197
|
return cwd;
|
|
144
198
|
}
|
|
145
199
|
|
|
200
|
+
function resolveProjectRoot(appRoot: string, cwd = process.cwd()): string {
|
|
201
|
+
const envAppRoot = String(process.env.SILICACLAW_APP_DIR || "").trim();
|
|
202
|
+
if (
|
|
203
|
+
envAppRoot &&
|
|
204
|
+
existsSync(resolve(envAppRoot, "apps", "local-console", "package.json")) &&
|
|
205
|
+
existsSync(resolve(envAppRoot, "package.json"))
|
|
206
|
+
) {
|
|
207
|
+
return resolve(envAppRoot);
|
|
208
|
+
}
|
|
209
|
+
const envRoot = String(process.env.SILICACLAW_WORKSPACE_DIR || "").trim();
|
|
210
|
+
if (envRoot) {
|
|
211
|
+
return resolve(envRoot);
|
|
212
|
+
}
|
|
213
|
+
if (
|
|
214
|
+
existsSync(resolve(appRoot, "apps", "local-console", "package.json")) &&
|
|
215
|
+
existsSync(resolve(appRoot, "package.json"))
|
|
216
|
+
) {
|
|
217
|
+
return appRoot;
|
|
218
|
+
}
|
|
219
|
+
if (!existsSync(resolve(cwd, "apps", "local-console", "package.json"))) {
|
|
220
|
+
return resolve(cwd);
|
|
221
|
+
}
|
|
222
|
+
return appRoot;
|
|
223
|
+
}
|
|
224
|
+
|
|
146
225
|
function resolveStorageRoot(workspaceRoot: string, cwd = process.cwd()): string {
|
|
226
|
+
const home = process.env.HOME || homedir();
|
|
227
|
+
if (home) {
|
|
228
|
+
return resolve(home, ".silicaclaw", "local-console");
|
|
229
|
+
}
|
|
147
230
|
const appRoot = resolve(workspaceRoot, "apps", "local-console");
|
|
148
231
|
if (existsSync(resolve(appRoot, "package.json"))) {
|
|
149
232
|
return appRoot;
|
|
@@ -151,6 +234,13 @@ function resolveStorageRoot(workspaceRoot: string, cwd = process.cwd()): string
|
|
|
151
234
|
return cwd;
|
|
152
235
|
}
|
|
153
236
|
|
|
237
|
+
function defaultOpenClawSourceDir(rootDir: string): string {
|
|
238
|
+
if (existsSync(resolve(rootDir, "openclaw.mjs")) || existsSync(resolve(rootDir, "package.json"))) {
|
|
239
|
+
return rootDir;
|
|
240
|
+
}
|
|
241
|
+
return resolve(rootDir, "..", "openclaw");
|
|
242
|
+
}
|
|
243
|
+
|
|
154
244
|
function resolveExecutableInPath(binName: string): string | null {
|
|
155
245
|
const pathValue = String(process.env.PATH || "").trim();
|
|
156
246
|
if (!pathValue) return null;
|
|
@@ -213,6 +303,50 @@ function summarizeSkillReadme(filePath: string) {
|
|
|
213
303
|
}
|
|
214
304
|
}
|
|
215
305
|
|
|
306
|
+
function readDialogueCheatsheetPreview(filePath: string, limit = 6) {
|
|
307
|
+
if (!filePath || !existsSync(filePath)) return [];
|
|
308
|
+
try {
|
|
309
|
+
return readFileSync(filePath, "utf8")
|
|
310
|
+
.split(/\r?\n/)
|
|
311
|
+
.map((line) => line.trim())
|
|
312
|
+
.filter((line) => line.startsWith("- "))
|
|
313
|
+
.map((line) => line.slice(2).trim())
|
|
314
|
+
.filter(Boolean)
|
|
315
|
+
.slice(0, limit);
|
|
316
|
+
} catch {
|
|
317
|
+
return [];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function readDialogueCheatsheetSections(filePath: string, maxSections = 3, maxItemsPerSection = 5) {
|
|
322
|
+
if (!filePath || !existsSync(filePath)) return [];
|
|
323
|
+
try {
|
|
324
|
+
const lines = readFileSync(filePath, "utf8").split(/\r?\n/);
|
|
325
|
+
const sections: Array<{ title: string; items: string[] }> = [];
|
|
326
|
+
let current: { title: string; items: string[] } | null = null;
|
|
327
|
+
for (const rawLine of lines) {
|
|
328
|
+
const line = rawLine.trim();
|
|
329
|
+
if (line.startsWith("## ")) {
|
|
330
|
+
if (current && current.items.length) sections.push(current);
|
|
331
|
+
current = { title: line.slice(3).trim(), items: [] };
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (line.startsWith("- ")) {
|
|
335
|
+
if (!current) {
|
|
336
|
+
current = { title: "Examples", items: [] };
|
|
337
|
+
}
|
|
338
|
+
if (current.items.length < maxItemsPerSection) {
|
|
339
|
+
current.items.push(line.slice(2).trim());
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (current && current.items.length) sections.push(current);
|
|
344
|
+
return sections.slice(0, maxSections);
|
|
345
|
+
} catch {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
216
350
|
function detectOpenClawInstallation(workspaceRoot: string) {
|
|
217
351
|
const workspaceDir = resolve(workspaceRoot, ".openclaw");
|
|
218
352
|
const homeDir = resolve(process.env.HOME || "", ".openclaw");
|
|
@@ -265,7 +399,7 @@ function detectOpenClawInstallation(workspaceRoot: string) {
|
|
|
265
399
|
|
|
266
400
|
function readOpenClawConfiguredGateway(workspaceRoot: string) {
|
|
267
401
|
const configuredSourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
|
|
268
|
-
const defaultSourceDir =
|
|
402
|
+
const defaultSourceDir = defaultOpenClawSourceDir(workspaceRoot);
|
|
269
403
|
const sourceDir = configuredSourceDir || defaultSourceDir;
|
|
270
404
|
const homeDir = resolve(process.env.HOME || "", ".openclaw");
|
|
271
405
|
const explicitConfigPath = String(process.env.OPENCLAW_CONFIG_PATH || "").trim();
|
|
@@ -453,7 +587,7 @@ function detectOwnerDeliveryStatus(params: {
|
|
|
453
587
|
const ownerAccount = String(process.env.OPENCLAW_OWNER_ACCOUNT || "").trim();
|
|
454
588
|
const explicitOpenClawBin = String(process.env.OPENCLAW_BIN || "").trim();
|
|
455
589
|
const configuredSourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
|
|
456
|
-
const defaultSourceDir =
|
|
590
|
+
const defaultSourceDir = defaultOpenClawSourceDir(params.workspaceRoot);
|
|
457
591
|
const openclawSourceDir = configuredSourceDir || defaultSourceDir;
|
|
458
592
|
const openclawSourceEntry = existingPathOrNull(resolve(openclawSourceDir, "openclaw.mjs"));
|
|
459
593
|
const openclawCommandResolvable = Boolean(explicitOpenClawBin || resolveExecutableInPath("openclaw") || openclawSourceEntry);
|
|
@@ -512,10 +646,18 @@ function hasMeaningfulJson(filePath: string): boolean {
|
|
|
512
646
|
}
|
|
513
647
|
}
|
|
514
648
|
|
|
515
|
-
function migrateLegacyDataIfNeeded(
|
|
516
|
-
const
|
|
649
|
+
function migrateLegacyDataIfNeeded(appRoot: string, projectRoot: string, storageRoot: string): void {
|
|
650
|
+
const homeDir = process.env.HOME || homedir();
|
|
651
|
+
const legacyNpxAppRoots = collectLegacyNpxAppRoots(homeDir);
|
|
517
652
|
const targetDataDir = resolve(storageRoot, "data");
|
|
518
|
-
|
|
653
|
+
const legacyDataDirs = [
|
|
654
|
+
resolve(appRoot, "data"),
|
|
655
|
+
resolve(appRoot, "apps", "local-console", "data"),
|
|
656
|
+
resolve(projectRoot, "data"),
|
|
657
|
+
resolve(projectRoot, "apps", "local-console", "data"),
|
|
658
|
+
resolve(process.cwd(), "data"),
|
|
659
|
+
...legacyNpxAppRoots.map((root) => resolve(root, "apps", "local-console", "data")),
|
|
660
|
+
].filter((dir, index, list) => list.indexOf(dir) === index && dir !== targetDataDir);
|
|
519
661
|
const files = [
|
|
520
662
|
"identity.json",
|
|
521
663
|
"profile.json",
|
|
@@ -525,16 +667,65 @@ function migrateLegacyDataIfNeeded(workspaceRoot: string, storageRoot: string):
|
|
|
525
667
|
"social-message-observations.json",
|
|
526
668
|
];
|
|
527
669
|
for (const file of files) {
|
|
528
|
-
const src = resolve(legacyDataDir, file);
|
|
529
670
|
const dst = resolve(targetDataDir, file);
|
|
530
|
-
if (!existsSync(src)) continue;
|
|
531
671
|
if (hasMeaningfulJson(dst)) continue;
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
672
|
+
for (const legacyDataDir of legacyDataDirs) {
|
|
673
|
+
const src = resolve(legacyDataDir, file);
|
|
674
|
+
if (!existsSync(src)) continue;
|
|
675
|
+
if (!hasMeaningfulJson(src)) continue;
|
|
676
|
+
mkdirSync(targetDataDir, { recursive: true });
|
|
677
|
+
copyFileSync(src, dst);
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const targetDotDir = resolve(storageRoot, ".silicaclaw");
|
|
683
|
+
const legacyDotDirs = [
|
|
684
|
+
resolve(appRoot, ".silicaclaw"),
|
|
685
|
+
resolve(appRoot, "apps", "local-console", ".silicaclaw"),
|
|
686
|
+
resolve(projectRoot, ".silicaclaw"),
|
|
687
|
+
resolve(projectRoot, "apps", "local-console", ".silicaclaw"),
|
|
688
|
+
resolve(process.cwd(), ".silicaclaw"),
|
|
689
|
+
...legacyNpxAppRoots.map((root) => resolve(root, "apps", "local-console", ".silicaclaw")),
|
|
690
|
+
].filter((dir, index, list) => list.indexOf(dir) === index && dir !== targetDotDir);
|
|
691
|
+
const dotFiles = ["social.runtime.json", "social.message-governance.json"];
|
|
692
|
+
for (const file of dotFiles) {
|
|
693
|
+
const dst = resolve(targetDotDir, file);
|
|
694
|
+
if (hasMeaningfulJson(dst)) continue;
|
|
695
|
+
for (const legacyDotDir of legacyDotDirs) {
|
|
696
|
+
const src = resolve(legacyDotDir, file);
|
|
697
|
+
if (!existsSync(src)) continue;
|
|
698
|
+
if (!hasMeaningfulJson(src)) continue;
|
|
699
|
+
mkdirSync(targetDotDir, { recursive: true });
|
|
700
|
+
copyFileSync(src, dst);
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
535
703
|
}
|
|
536
704
|
}
|
|
537
705
|
|
|
706
|
+
function collectLegacyNpxAppRoots(homeDir: string): string[] {
|
|
707
|
+
const cacheRoots = [
|
|
708
|
+
resolve(homeDir, ".silicaclaw", "npm-cache", "_npx"),
|
|
709
|
+
resolve(homeDir, ".npm", "_npx"),
|
|
710
|
+
];
|
|
711
|
+
const roots: string[] = [];
|
|
712
|
+
for (const cacheRoot of cacheRoots) {
|
|
713
|
+
if (!existsSync(cacheRoot)) continue;
|
|
714
|
+
let entries: string[] = [];
|
|
715
|
+
try {
|
|
716
|
+
entries = readdirSync(cacheRoot);
|
|
717
|
+
} catch {
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
for (const entry of entries) {
|
|
721
|
+
const candidate = resolve(cacheRoot, entry, "node_modules", "@silicaclaw", "cli");
|
|
722
|
+
if (!existsSync(resolve(candidate, "apps", "local-console"))) continue;
|
|
723
|
+
roots.push(candidate);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return Array.from(new Set(roots));
|
|
727
|
+
}
|
|
728
|
+
|
|
538
729
|
function parseListEnv(raw: string): string[] {
|
|
539
730
|
return raw
|
|
540
731
|
.split(/[,\n]/g)
|
|
@@ -699,6 +890,7 @@ type OpenClawBridgeConfigView = {
|
|
|
699
890
|
|
|
700
891
|
export class LocalNodeService {
|
|
701
892
|
private workspaceRoot: string;
|
|
893
|
+
private projectRoot: string;
|
|
702
894
|
private storageRoot: string;
|
|
703
895
|
private identityRepo: IdentityRepo;
|
|
704
896
|
private profileRepo: ProfileRepo;
|
|
@@ -741,7 +933,7 @@ export class LocalNodeService {
|
|
|
741
933
|
|
|
742
934
|
private network: NetworkAdapter;
|
|
743
935
|
private adapterMode: "mock" | "local-event-bus" | "real-preview" | "webrtc-preview" | "relay-preview";
|
|
744
|
-
private networkMode: "local" | "lan" | "global-preview" =
|
|
936
|
+
private networkMode: "local" | "lan" | "global-preview" = DEFAULT_NETWORK_MODE;
|
|
745
937
|
private networkNamespace: string;
|
|
746
938
|
private networkPort: number | null;
|
|
747
939
|
private socialConfig: SocialConfig;
|
|
@@ -755,17 +947,22 @@ export class LocalNodeService {
|
|
|
755
947
|
"silicaclaw-existing";
|
|
756
948
|
private resolvedOpenClawIdentityPath: string | null = null;
|
|
757
949
|
private webrtcSignalingUrls: string[] = [];
|
|
758
|
-
private webrtcRoom =
|
|
950
|
+
private webrtcRoom = DEFAULT_GLOBAL_ROOM;
|
|
759
951
|
private webrtcSeedPeers: string[] = [];
|
|
760
952
|
private webrtcBootstrapHints: string[] = [];
|
|
761
953
|
private webrtcBootstrapSources: string[] = [];
|
|
954
|
+
private networkStarted = false;
|
|
955
|
+
private networkStartupError: string | null = null;
|
|
956
|
+
private networkReconnectTimer: NodeJS.Timeout | null = null;
|
|
957
|
+
private networkReconnectDelayMs = 5_000;
|
|
762
958
|
private appVersion = "unknown";
|
|
763
959
|
|
|
764
|
-
constructor(options?: { workspaceRoot?: string; storageRoot?: string }) {
|
|
960
|
+
constructor(options?: { workspaceRoot?: string; projectRoot?: string; storageRoot?: string }) {
|
|
765
961
|
this.workspaceRoot = options?.workspaceRoot || resolveWorkspaceRoot();
|
|
962
|
+
this.projectRoot = options?.projectRoot || resolveProjectRoot(this.workspaceRoot);
|
|
766
963
|
this.storageRoot = options?.storageRoot || resolveStorageRoot(this.workspaceRoot);
|
|
767
964
|
this.appVersion = readWorkspaceVersion(this.workspaceRoot);
|
|
768
|
-
migrateLegacyDataIfNeeded(this.workspaceRoot, this.storageRoot);
|
|
965
|
+
migrateLegacyDataIfNeeded(this.workspaceRoot, this.projectRoot, this.storageRoot);
|
|
769
966
|
|
|
770
967
|
this.identityRepo = new IdentityRepo(this.storageRoot);
|
|
771
968
|
this.profileRepo = new ProfileRepo(this.storageRoot);
|
|
@@ -777,16 +974,16 @@ export class LocalNodeService {
|
|
|
777
974
|
this.socialRuntimeRepo = new SocialRuntimeRepo(this.storageRoot);
|
|
778
975
|
this.messageGovernance = this.defaultMessageGovernance();
|
|
779
976
|
|
|
780
|
-
let loadedSocial = loadSocialConfig(this.
|
|
977
|
+
let loadedSocial = loadSocialConfig(this.projectRoot);
|
|
781
978
|
if (!loadedSocial.meta.found) {
|
|
782
|
-
ensureDefaultSocialMd(this.
|
|
979
|
+
ensureDefaultSocialMd(this.projectRoot, {
|
|
783
980
|
display_name: this.getDefaultDisplayName(),
|
|
784
981
|
bio: "Local AI agent connected to SilicaClaw",
|
|
785
982
|
tags: ["openclaw", "local-first"],
|
|
786
|
-
mode:
|
|
983
|
+
mode: DEFAULT_NETWORK_MODE,
|
|
787
984
|
public_enabled: false,
|
|
788
985
|
});
|
|
789
|
-
loadedSocial = loadSocialConfig(this.
|
|
986
|
+
loadedSocial = loadSocialConfig(this.projectRoot);
|
|
790
987
|
this.initState.social_auto_created = true;
|
|
791
988
|
}
|
|
792
989
|
this.socialConfig = loadedSocial.config;
|
|
@@ -795,8 +992,8 @@ export class LocalNodeService {
|
|
|
795
992
|
this.socialParseError = loadedSocial.meta.parse_error;
|
|
796
993
|
this.socialRawFrontmatter = loadedSocial.raw_frontmatter;
|
|
797
994
|
|
|
798
|
-
this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE ||
|
|
799
|
-
this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT ||
|
|
995
|
+
this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE || DEFAULT_NETWORK_NAMESPACE;
|
|
996
|
+
this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT || DEFAULT_NETWORK_PORT);
|
|
800
997
|
this.applyResolvedNetworkConfig();
|
|
801
998
|
const resolved = this.buildNetworkAdapter();
|
|
802
999
|
this.network = resolved.adapter;
|
|
@@ -808,32 +1005,19 @@ export class LocalNodeService {
|
|
|
808
1005
|
await this.hydrateFromDisk();
|
|
809
1006
|
|
|
810
1007
|
this.bindNetworkSubscriptions();
|
|
811
|
-
await this.
|
|
812
|
-
await this.log(
|
|
813
|
-
"info",
|
|
814
|
-
`Local node started (${this.adapterMode}, mode=${this.networkMode}, signaling=${this.webrtcSignalingUrls[0] || "-"}, room=${this.webrtcRoom})`
|
|
815
|
-
);
|
|
816
|
-
|
|
817
|
-
if (this.profile?.public_enabled && this.broadcastEnabled) {
|
|
818
|
-
try {
|
|
819
|
-
await this.broadcastNow("adapter_start");
|
|
820
|
-
} catch (error) {
|
|
821
|
-
await this.log(
|
|
822
|
-
"warn",
|
|
823
|
-
`Initial broadcast failed: ${error instanceof Error ? error.message : String(error)}`
|
|
824
|
-
);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
this.startBroadcastLoop();
|
|
1008
|
+
await this.startNetworkAdapterWithRetry("adapter_start");
|
|
829
1009
|
}
|
|
830
1010
|
|
|
831
1011
|
async stop(): Promise<void> {
|
|
1012
|
+
this.clearNetworkReconnectTimer();
|
|
832
1013
|
if (this.broadcaster) {
|
|
833
1014
|
clearInterval(this.broadcaster);
|
|
834
1015
|
this.broadcaster = null;
|
|
835
1016
|
}
|
|
836
|
-
|
|
1017
|
+
if (this.networkStarted) {
|
|
1018
|
+
await this.network.stop();
|
|
1019
|
+
}
|
|
1020
|
+
this.networkStarted = false;
|
|
837
1021
|
}
|
|
838
1022
|
|
|
839
1023
|
private ensureLocalDirectoryBaseline(): void {
|
|
@@ -848,12 +1032,8 @@ export class LocalNodeService {
|
|
|
848
1032
|
}
|
|
849
1033
|
|
|
850
1034
|
getOverview() {
|
|
851
|
-
this.
|
|
852
|
-
|
|
853
|
-
const profiles = Object.values(this.directory.profiles);
|
|
854
|
-
const onlineCount = profiles.filter((profile) =>
|
|
855
|
-
isAgentOnline(this.directory.presence[profile.agent_id], Date.now(), PRESENCE_TTL_MS)
|
|
856
|
-
).length;
|
|
1035
|
+
const discovered = this.search("");
|
|
1036
|
+
const onlineCount = discovered.filter((profile) => profile.online).length;
|
|
857
1037
|
|
|
858
1038
|
return {
|
|
859
1039
|
app_version: this.appVersion,
|
|
@@ -864,9 +1044,9 @@ export class LocalNodeService {
|
|
|
864
1044
|
last_broadcast_error_at: this.lastBroadcastErrorAt,
|
|
865
1045
|
last_broadcast_error: this.lastBroadcastError,
|
|
866
1046
|
broadcast_failure_count: this.broadcastFailureCount,
|
|
867
|
-
discovered_count:
|
|
1047
|
+
discovered_count: discovered.length,
|
|
868
1048
|
online_count: onlineCount,
|
|
869
|
-
offline_count: Math.max(0,
|
|
1049
|
+
offline_count: Math.max(0, discovered.length - onlineCount),
|
|
870
1050
|
init_state: this.initState,
|
|
871
1051
|
presence_ttl_ms: PRESENCE_TTL_MS,
|
|
872
1052
|
onboarding: this.getOnboardingSummary(),
|
|
@@ -888,7 +1068,9 @@ export class LocalNodeService {
|
|
|
888
1068
|
}
|
|
889
1069
|
|
|
890
1070
|
getNetworkSummary() {
|
|
891
|
-
const
|
|
1071
|
+
const network = this.getResolvedRealtimeNetworkSummary();
|
|
1072
|
+
const diagnostics = network.diagnostics;
|
|
1073
|
+
const relayCapable = this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview";
|
|
892
1074
|
const peerCount = diagnostics?.peers.total ?? 0;
|
|
893
1075
|
|
|
894
1076
|
return {
|
|
@@ -916,30 +1098,34 @@ export class LocalNodeService {
|
|
|
916
1098
|
real_preview_stats: diagnostics?.stats ?? null,
|
|
917
1099
|
real_preview_transport_stats: diagnostics?.transport_stats ?? null,
|
|
918
1100
|
real_preview_discovery_stats: diagnostics?.discovery_stats ?? null,
|
|
919
|
-
webrtc_preview:
|
|
1101
|
+
webrtc_preview: relayCapable
|
|
920
1102
|
? {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1103
|
+
started: this.networkStarted,
|
|
1104
|
+
startup_error: this.networkStartupError,
|
|
1105
|
+
signaling_url: network.signaling_url,
|
|
1106
|
+
signaling_endpoints: network.signaling_endpoints,
|
|
1107
|
+
room: network.room,
|
|
1108
|
+
bootstrap_sources: network.bootstrap_sources,
|
|
1109
|
+
seed_peers_count: network.seed_peers_count,
|
|
1110
|
+
discovery_events_total: diagnostics?.discovery_events_total ?? 0,
|
|
1111
|
+
last_discovery_event_at: diagnostics?.last_discovery_event_at ?? 0,
|
|
1112
|
+
active_webrtc_peers: diagnostics?.active_webrtc_peers ?? 0,
|
|
1113
|
+
reconnect_attempts_total: diagnostics?.reconnect_attempts_total ?? 0,
|
|
1114
|
+
last_join_at: diagnostics?.last_join_at ?? 0,
|
|
1115
|
+
last_poll_at: diagnostics?.last_poll_at ?? 0,
|
|
1116
|
+
last_publish_at: diagnostics?.last_publish_at ?? 0,
|
|
1117
|
+
last_peer_refresh_at: diagnostics?.last_peer_refresh_at ?? 0,
|
|
1118
|
+
last_error_at: diagnostics?.last_error_at ?? 0,
|
|
1119
|
+
last_error: diagnostics?.last_error ?? null,
|
|
936
1120
|
}
|
|
937
1121
|
: null,
|
|
938
1122
|
};
|
|
939
1123
|
}
|
|
940
1124
|
|
|
941
1125
|
getNetworkConfig() {
|
|
942
|
-
const
|
|
1126
|
+
const network = this.getResolvedRealtimeNetworkSummary();
|
|
1127
|
+
const diagnostics = network.diagnostics;
|
|
1128
|
+
const relayCapable = this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview";
|
|
943
1129
|
return {
|
|
944
1130
|
adapter: this.adapterMode,
|
|
945
1131
|
mode: this.networkMode,
|
|
@@ -953,23 +1139,25 @@ export class LocalNodeService {
|
|
|
953
1139
|
},
|
|
954
1140
|
limits: diagnostics?.limits ?? null,
|
|
955
1141
|
adapter_config: diagnostics?.config ?? null,
|
|
956
|
-
adapter_extra:
|
|
1142
|
+
adapter_extra: relayCapable
|
|
957
1143
|
? {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1144
|
+
started: this.networkStarted,
|
|
1145
|
+
startup_error: this.networkStartupError,
|
|
1146
|
+
signaling_url: network.signaling_url,
|
|
1147
|
+
signaling_endpoints: network.signaling_endpoints,
|
|
1148
|
+
room: network.room,
|
|
1149
|
+
bootstrap_sources: network.bootstrap_sources,
|
|
1150
|
+
seed_peers_count: network.seed_peers_count,
|
|
1151
|
+
discovery_events_total: diagnostics?.discovery_events_total ?? 0,
|
|
1152
|
+
last_discovery_event_at: diagnostics?.last_discovery_event_at ?? 0,
|
|
1153
|
+
connection_states_summary: diagnostics?.connection_states_summary ?? null,
|
|
1154
|
+
datachannel_states_summary: diagnostics?.datachannel_states_summary ?? null,
|
|
1155
|
+
last_join_at: diagnostics?.last_join_at ?? 0,
|
|
1156
|
+
last_poll_at: diagnostics?.last_poll_at ?? 0,
|
|
1157
|
+
last_publish_at: diagnostics?.last_publish_at ?? 0,
|
|
1158
|
+
last_peer_refresh_at: diagnostics?.last_peer_refresh_at ?? 0,
|
|
1159
|
+
last_error_at: diagnostics?.last_error_at ?? 0,
|
|
1160
|
+
last_error: diagnostics?.last_error ?? null,
|
|
973
1161
|
}
|
|
974
1162
|
: null,
|
|
975
1163
|
env: {
|
|
@@ -997,14 +1185,16 @@ export class LocalNodeService {
|
|
|
997
1185
|
this.adapterMode === "real-preview"
|
|
998
1186
|
? "lan-preview"
|
|
999
1187
|
: this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview"
|
|
1000
|
-
? "
|
|
1188
|
+
? "global-preview"
|
|
1001
1189
|
: "local-process",
|
|
1002
1190
|
mode_explainer: this.getModeExplainer(),
|
|
1003
1191
|
};
|
|
1004
1192
|
}
|
|
1005
1193
|
|
|
1006
1194
|
getNetworkStats() {
|
|
1007
|
-
const
|
|
1195
|
+
const network = this.getResolvedRealtimeNetworkSummary();
|
|
1196
|
+
const diagnostics = network.diagnostics;
|
|
1197
|
+
const relayCapable = this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview";
|
|
1008
1198
|
const peers: Array<{ status?: string }> = diagnostics?.peers?.items ?? [];
|
|
1009
1199
|
const online = peers.filter((peer: { status?: string }) => peer.status === "online").length;
|
|
1010
1200
|
|
|
@@ -1031,34 +1221,37 @@ export class LocalNodeService {
|
|
|
1031
1221
|
adapter_stats: diagnostics?.stats ?? null,
|
|
1032
1222
|
adapter_transport_stats: diagnostics?.transport_stats ?? null,
|
|
1033
1223
|
adapter_discovery_stats: diagnostics?.discovery_stats ?? null,
|
|
1034
|
-
adapter_diagnostics_summary: diagnostics
|
|
1224
|
+
adapter_diagnostics_summary: relayCapable || diagnostics
|
|
1035
1225
|
? {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1226
|
+
started: this.networkStarted,
|
|
1227
|
+
startup_error: this.networkStartupError,
|
|
1228
|
+
signaling_url: network.signaling_url,
|
|
1229
|
+
signaling_endpoints: network.signaling_endpoints,
|
|
1230
|
+
room: network.room,
|
|
1231
|
+
bootstrap_sources: network.bootstrap_sources,
|
|
1232
|
+
seed_peers_count: network.seed_peers_count,
|
|
1233
|
+
discovery_events_total: diagnostics?.discovery_events_total ?? 0,
|
|
1234
|
+
last_discovery_event_at: diagnostics?.last_discovery_event_at ?? 0,
|
|
1235
|
+
connection_states_summary: diagnostics?.connection_states_summary ?? null,
|
|
1236
|
+
datachannel_states_summary: diagnostics?.datachannel_states_summary ?? null,
|
|
1237
|
+
signaling_messages_sent_total: diagnostics?.signaling_messages_sent_total ?? null,
|
|
1238
|
+
signaling_messages_received_total: diagnostics?.signaling_messages_received_total ?? null,
|
|
1239
|
+
reconnect_attempts_total: diagnostics?.reconnect_attempts_total ?? null,
|
|
1240
|
+
active_webrtc_peers: diagnostics?.active_webrtc_peers ?? null,
|
|
1241
|
+
last_join_at: diagnostics?.last_join_at ?? 0,
|
|
1242
|
+
last_poll_at: diagnostics?.last_poll_at ?? 0,
|
|
1243
|
+
last_publish_at: diagnostics?.last_publish_at ?? 0,
|
|
1244
|
+
last_peer_refresh_at: diagnostics?.last_peer_refresh_at ?? 0,
|
|
1245
|
+
last_error_at: diagnostics?.last_error_at ?? 0,
|
|
1246
|
+
last_error: diagnostics?.last_error ?? null,
|
|
1055
1247
|
}
|
|
1056
1248
|
: null,
|
|
1057
1249
|
};
|
|
1058
1250
|
}
|
|
1059
1251
|
|
|
1060
1252
|
getPeersSummary() {
|
|
1061
|
-
const
|
|
1253
|
+
const network = this.getResolvedRealtimeNetworkSummary();
|
|
1254
|
+
const diagnostics = network.diagnostics;
|
|
1062
1255
|
if (!diagnostics) {
|
|
1063
1256
|
return {
|
|
1064
1257
|
adapter: this.adapterMode,
|
|
@@ -1081,11 +1274,13 @@ export class LocalNodeService {
|
|
|
1081
1274
|
components: diagnostics.components,
|
|
1082
1275
|
limits: diagnostics.limits,
|
|
1083
1276
|
diagnostics_summary: {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1277
|
+
started: this.networkStarted,
|
|
1278
|
+
startup_error: this.networkStartupError,
|
|
1279
|
+
signaling_url: network.signaling_url,
|
|
1280
|
+
signaling_endpoints: network.signaling_endpoints,
|
|
1281
|
+
room: network.room,
|
|
1282
|
+
bootstrap_sources: network.bootstrap_sources,
|
|
1283
|
+
seed_peers_count: network.seed_peers_count,
|
|
1089
1284
|
discovery_events_total: diagnostics.discovery_events_total ?? 0,
|
|
1090
1285
|
last_discovery_event_at: diagnostics.last_discovery_event_at ?? 0,
|
|
1091
1286
|
connection_states_summary: diagnostics.connection_states_summary ?? null,
|
|
@@ -1132,11 +1327,12 @@ export class LocalNodeService {
|
|
|
1132
1327
|
getRuntimePaths() {
|
|
1133
1328
|
return {
|
|
1134
1329
|
workspace_root: this.workspaceRoot,
|
|
1330
|
+
project_root: this.projectRoot,
|
|
1135
1331
|
storage_root: this.storageRoot,
|
|
1136
1332
|
data_dir: resolve(this.storageRoot, "data"),
|
|
1137
1333
|
social_runtime_path: resolve(this.storageRoot, ".silicaclaw", "social.runtime.json"),
|
|
1138
1334
|
local_console_public_dir: resolve(this.workspaceRoot, "apps", "local-console", "public"),
|
|
1139
|
-
social_lookup_paths: getSocialConfigSearchPaths(this.
|
|
1335
|
+
social_lookup_paths: getSocialConfigSearchPaths(this.projectRoot),
|
|
1140
1336
|
social_source_path: this.socialSourcePath,
|
|
1141
1337
|
};
|
|
1142
1338
|
}
|
|
@@ -1175,9 +1371,10 @@ export class LocalNodeService {
|
|
|
1175
1371
|
|
|
1176
1372
|
getIntegrationStatus(): IntegrationStatusSummary {
|
|
1177
1373
|
const runtimeGenerated = Boolean(this.socialRuntime && this.socialRuntime.last_loaded_at > 0);
|
|
1178
|
-
const
|
|
1179
|
-
const
|
|
1180
|
-
const
|
|
1374
|
+
const runtimeReady = this.socialFound && runtimeGenerated && !this.socialParseError;
|
|
1375
|
+
const connected = runtimeReady && this.networkStarted;
|
|
1376
|
+
const configured = runtimeReady && this.socialConfig.enabled;
|
|
1377
|
+
const running = configured && this.broadcastEnabled && this.networkStarted;
|
|
1181
1378
|
const publicEnabled = Boolean(this.profile?.public_enabled);
|
|
1182
1379
|
const discoveryEnabled =
|
|
1183
1380
|
this.socialConfig.discovery.discoverable &&
|
|
@@ -1199,6 +1396,10 @@ export class LocalNodeService {
|
|
|
1199
1396
|
? "running"
|
|
1200
1397
|
: !configured
|
|
1201
1398
|
? "not configured"
|
|
1399
|
+
: this.networkReconnectTimer
|
|
1400
|
+
? "reconnecting to relay"
|
|
1401
|
+
: this.networkStartupError
|
|
1402
|
+
? this.networkStartupError
|
|
1202
1403
|
: !this.broadcastEnabled
|
|
1203
1404
|
? "broadcast paused"
|
|
1204
1405
|
: "not running";
|
|
@@ -1244,20 +1445,36 @@ export class LocalNodeService {
|
|
|
1244
1445
|
}
|
|
1245
1446
|
|
|
1246
1447
|
async setNetworkModeRuntime(mode: "local" | "lan" | "global-preview") {
|
|
1247
|
-
const
|
|
1448
|
+
const before = {
|
|
1449
|
+
mode: this.networkMode,
|
|
1450
|
+
adapter: this.adapterMode,
|
|
1451
|
+
namespace: this.networkNamespace,
|
|
1452
|
+
port: this.networkPort,
|
|
1453
|
+
};
|
|
1248
1454
|
if (mode !== "local" && mode !== "lan" && mode !== "global-preview") {
|
|
1249
1455
|
throw new Error("invalid_network_mode");
|
|
1250
1456
|
}
|
|
1251
1457
|
this.socialConfig.network.mode = mode;
|
|
1252
1458
|
this.socialConfig.network.adapter = this.adapterForMode(mode);
|
|
1253
1459
|
this.applyResolvedNetworkConfig();
|
|
1254
|
-
|
|
1460
|
+
|
|
1461
|
+
const needsRestart =
|
|
1462
|
+
before.mode !== this.networkMode ||
|
|
1463
|
+
before.adapter !== this.socialConfig.network.adapter ||
|
|
1464
|
+
before.namespace !== this.networkNamespace ||
|
|
1465
|
+
(before.port ?? null) !== (this.networkPort ?? null);
|
|
1466
|
+
|
|
1467
|
+
if (needsRestart) {
|
|
1468
|
+
await this.restartNetworkAdapter("set_network_mode_runtime");
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
this.socialNetworkRequiresRestart = false;
|
|
1255
1472
|
await this.writeSocialRuntime();
|
|
1256
1473
|
return {
|
|
1257
1474
|
mode: this.networkMode,
|
|
1258
|
-
adapter: this.
|
|
1259
|
-
network_requires_restart:
|
|
1260
|
-
note: "Runtime mode updated. Existing social.md is unchanged.",
|
|
1475
|
+
adapter: this.adapterMode,
|
|
1476
|
+
network_requires_restart: false,
|
|
1477
|
+
note: "Runtime mode updated and adapter restarted. Existing social.md is unchanged.",
|
|
1261
1478
|
};
|
|
1262
1479
|
}
|
|
1263
1480
|
|
|
@@ -1272,7 +1489,7 @@ export class LocalNodeService {
|
|
|
1272
1489
|
this.socialConfig.network.adapter = "relay-preview";
|
|
1273
1490
|
this.socialConfig.network.signaling_url = signalingUrl;
|
|
1274
1491
|
this.socialConfig.network.signaling_urls = [signalingUrl];
|
|
1275
|
-
this.socialConfig.network.room = room ||
|
|
1492
|
+
this.socialConfig.network.room = room || DEFAULT_GLOBAL_ROOM;
|
|
1276
1493
|
this.applyResolvedNetworkConfig();
|
|
1277
1494
|
await this.restartNetworkAdapter("quick_connect_global_preview");
|
|
1278
1495
|
this.socialNetworkRequiresRestart = false;
|
|
@@ -1297,7 +1514,7 @@ export class LocalNodeService {
|
|
|
1297
1514
|
port: this.networkPort,
|
|
1298
1515
|
};
|
|
1299
1516
|
|
|
1300
|
-
const loaded = loadSocialConfig(this.
|
|
1517
|
+
const loaded = loadSocialConfig(this.projectRoot);
|
|
1301
1518
|
this.socialConfig = loaded.config;
|
|
1302
1519
|
this.socialSourcePath = loaded.meta.source_path;
|
|
1303
1520
|
this.socialFound = loaded.meta.found;
|
|
@@ -1319,13 +1536,18 @@ export class LocalNodeService {
|
|
|
1319
1536
|
before.namespace !== after.namespace ||
|
|
1320
1537
|
(before.port ?? null) !== (after.port ?? null);
|
|
1321
1538
|
|
|
1539
|
+
if (this.socialNetworkRequiresRestart) {
|
|
1540
|
+
await this.restartNetworkAdapter("reload_social_config");
|
|
1541
|
+
this.socialNetworkRequiresRestart = false;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1322
1544
|
await this.writeSocialRuntime();
|
|
1323
1545
|
|
|
1324
1546
|
return this.getSocialConfigView();
|
|
1325
1547
|
}
|
|
1326
1548
|
|
|
1327
1549
|
async generateDefaultSocialMd() {
|
|
1328
|
-
const result = ensureDefaultSocialMd(this.
|
|
1550
|
+
const result = ensureDefaultSocialMd(this.projectRoot, {
|
|
1329
1551
|
display_name: this.getDefaultDisplayName(),
|
|
1330
1552
|
bio: "Local AI agent connected to SilicaClaw",
|
|
1331
1553
|
tags: ["openclaw", "local-first"],
|
|
@@ -1352,10 +1574,11 @@ export class LocalNodeService {
|
|
|
1352
1574
|
search(keyword: string): PublicProfileSummary[] {
|
|
1353
1575
|
this.ensureLocalDirectoryBaseline();
|
|
1354
1576
|
this.compactCacheInMemory();
|
|
1355
|
-
|
|
1577
|
+
const directMatches = searchDirectory(this.directory, keyword, { presenceTTLms: PRESENCE_TTL_MS }).map((profile) => {
|
|
1356
1578
|
const lastSeenAt = this.directory.presence[profile.agent_id] ?? 0;
|
|
1357
1579
|
return this.toPublicProfileSummary(profile, { last_seen_at: lastSeenAt });
|
|
1358
1580
|
});
|
|
1581
|
+
return this.mergeMessageOnlyAgentSummaries(directMatches, keyword);
|
|
1359
1582
|
}
|
|
1360
1583
|
|
|
1361
1584
|
getPublicProfilePreview(): PublicProfileSummary | null {
|
|
@@ -1433,11 +1656,11 @@ export class LocalNodeService {
|
|
|
1433
1656
|
|
|
1434
1657
|
getOpenClawBridgeStatus(): OpenClawBridgeStatus {
|
|
1435
1658
|
const integration = this.getIntegrationStatus();
|
|
1436
|
-
const openclawInstallation = detectOpenClawInstallation(this.
|
|
1437
|
-
const openclawRuntime = detectOpenClawRuntime(this.
|
|
1659
|
+
const openclawInstallation = detectOpenClawInstallation(this.projectRoot);
|
|
1660
|
+
const openclawRuntime = detectOpenClawRuntime(this.projectRoot);
|
|
1438
1661
|
const skillInstallation = detectOpenClawSkillInstallation();
|
|
1439
1662
|
const ownerDelivery = detectOwnerDeliveryStatus({
|
|
1440
|
-
workspaceRoot: this.
|
|
1663
|
+
workspaceRoot: this.projectRoot,
|
|
1441
1664
|
connectedToSilicaclaw: integration.connected_to_silicaclaw,
|
|
1442
1665
|
openclawRunning: openclawRuntime.running,
|
|
1443
1666
|
skillInstalled: skillInstallation.installed,
|
|
@@ -1500,11 +1723,15 @@ export class LocalNodeService {
|
|
|
1500
1723
|
};
|
|
1501
1724
|
}
|
|
1502
1725
|
|
|
1503
|
-
async installOpenClawSkill() {
|
|
1726
|
+
async installOpenClawSkill(skillName?: string) {
|
|
1504
1727
|
const scriptPath = resolve(this.workspaceRoot, "scripts", "install-openclaw-skill.mjs");
|
|
1505
|
-
const
|
|
1728
|
+
const args = [scriptPath];
|
|
1729
|
+
if (skillName) {
|
|
1730
|
+
args.push(`--skill=${skillName}`);
|
|
1731
|
+
}
|
|
1732
|
+
const { stdout } = await execFileAsync(process.execPath, args, {
|
|
1506
1733
|
cwd: this.workspaceRoot,
|
|
1507
|
-
env: process.env,
|
|
1734
|
+
env: { ...process.env, SILICACLAW_WORKSPACE_DIR: this.projectRoot },
|
|
1508
1735
|
maxBuffer: 1024 * 1024,
|
|
1509
1736
|
});
|
|
1510
1737
|
const parsed = JSON.parse(String(stdout || "{}"));
|
|
@@ -1528,12 +1755,12 @@ export class LocalNodeService {
|
|
|
1528
1755
|
const homeDir = resolve(process.env.HOME || "", ".openclaw");
|
|
1529
1756
|
const workspaceSkillDir = resolve(homeDir, "workspace", "skills");
|
|
1530
1757
|
const legacySkillDir = resolve(homeDir, "skills");
|
|
1531
|
-
const openclawSourceDir =
|
|
1532
|
-
const openclawRuntime = detectOpenClawRuntime(this.
|
|
1758
|
+
const openclawSourceDir = defaultOpenClawSourceDir(this.projectRoot);
|
|
1759
|
+
const openclawRuntime = detectOpenClawRuntime(this.projectRoot);
|
|
1533
1760
|
|
|
1534
1761
|
return {
|
|
1535
|
-
bridge_api_base:
|
|
1536
|
-
openclaw_detected: detectOpenClawInstallation(this.
|
|
1762
|
+
bridge_api_base: DEFAULT_BRIDGE_API_BASE,
|
|
1763
|
+
openclaw_detected: detectOpenClawInstallation(this.projectRoot).detected,
|
|
1537
1764
|
openclaw_running: openclawRuntime.running,
|
|
1538
1765
|
openclaw_gateway_host: OPENCLAW_GATEWAY_HOST,
|
|
1539
1766
|
openclaw_gateway_port: openclawRuntime.configured_gateway_port,
|
|
@@ -1542,7 +1769,7 @@ export class LocalNodeService {
|
|
|
1542
1769
|
openclaw_workspace_skill_dir: workspaceSkillDir,
|
|
1543
1770
|
openclaw_legacy_skill_dir: legacySkillDir,
|
|
1544
1771
|
silicaclaw_env_template_path: resolve(this.workspaceRoot, "openclaw-owner-forward.env.example"),
|
|
1545
|
-
recommended_skill_name: "silicaclaw-
|
|
1772
|
+
recommended_skill_name: "silicaclaw-bridge-setup",
|
|
1546
1773
|
recommended_install_command: "silicaclaw openclaw-skill-install",
|
|
1547
1774
|
recommended_owner_forward_env: {
|
|
1548
1775
|
OPENCLAW_SOURCE_DIR: openclawSourceDir,
|
|
@@ -1560,6 +1787,7 @@ export class LocalNodeService {
|
|
|
1560
1787
|
].join(" "),
|
|
1561
1788
|
notes: [
|
|
1562
1789
|
"Install and maintain the skill from SilicaClaw; do not edit OpenClaw core source for this integration.",
|
|
1790
|
+
"Use silicaclaw-bridge-setup first when OpenClaw still needs local install, readiness checks, or troubleshooting guidance.",
|
|
1563
1791
|
"OpenClaw learns broadcasts via the installed skill under ~/.openclaw/workspace/skills/.",
|
|
1564
1792
|
"Runtime detection prefers the actual OpenClaw gateway listener port, then falls back to OpenClaw's own openclaw.json gateway.port.",
|
|
1565
1793
|
"Owner delivery runs through OpenClaw's own message channel stack after the skill forwards a summary.",
|
|
@@ -1579,6 +1807,12 @@ export class LocalNodeService {
|
|
|
1579
1807
|
const skillPath = resolve(dir.path, "SKILL.md");
|
|
1580
1808
|
const versionPath = resolve(dir.path, "VERSION");
|
|
1581
1809
|
const manifest = readJsonFileSafe(manifestPath);
|
|
1810
|
+
const references = (manifest?.references && typeof manifest.references === "object")
|
|
1811
|
+
? manifest.references as Record<string, unknown>
|
|
1812
|
+
: null;
|
|
1813
|
+
const ownerDialogueCheatsheetPath = references?.owner_dialogue_cheatsheet_zh
|
|
1814
|
+
? resolve(dir.path, String(references.owner_dialogue_cheatsheet_zh))
|
|
1815
|
+
: null;
|
|
1582
1816
|
const name = String(manifest?.name || dir.name);
|
|
1583
1817
|
const capabilities = Array.isArray(manifest?.capabilities)
|
|
1584
1818
|
? manifest.capabilities.map((item) => String(item))
|
|
@@ -1598,6 +1832,9 @@ export class LocalNodeService {
|
|
|
1598
1832
|
skill_path: existsSync(skillPath) ? skillPath : null,
|
|
1599
1833
|
capabilities,
|
|
1600
1834
|
transport: manifest?.transport || null,
|
|
1835
|
+
owner_dialogue_cheatsheet_path: ownerDialogueCheatsheetPath && existsSync(ownerDialogueCheatsheetPath) ? ownerDialogueCheatsheetPath : null,
|
|
1836
|
+
owner_dialogue_examples_zh: ownerDialogueCheatsheetPath ? readDialogueCheatsheetPreview(ownerDialogueCheatsheetPath) : [],
|
|
1837
|
+
owner_dialogue_sections_zh: ownerDialogueCheatsheetPath ? readDialogueCheatsheetSections(ownerDialogueCheatsheetPath) : [],
|
|
1601
1838
|
installed_in_openclaw: installedInWorkspace || installedInLegacy,
|
|
1602
1839
|
install_mode: installedInWorkspace ? "workspace" : installedInLegacy ? "legacy" : "not_installed",
|
|
1603
1840
|
installed_path: installedInWorkspace ? installedWorkspacePath : installedInLegacy ? installedLegacyPath : null,
|
|
@@ -1612,6 +1849,12 @@ export class LocalNodeService {
|
|
|
1612
1849
|
const skillPath = resolve(dir.path, "SKILL.md");
|
|
1613
1850
|
const versionPath = resolve(dir.path, "VERSION");
|
|
1614
1851
|
const manifest = readJsonFileSafe(manifestPath);
|
|
1852
|
+
const references = (manifest?.references && typeof manifest.references === "object")
|
|
1853
|
+
? manifest.references as Record<string, unknown>
|
|
1854
|
+
: null;
|
|
1855
|
+
const ownerDialogueCheatsheetPath = references?.owner_dialogue_cheatsheet_zh
|
|
1856
|
+
? resolve(dir.path, String(references.owner_dialogue_cheatsheet_zh))
|
|
1857
|
+
: null;
|
|
1615
1858
|
return {
|
|
1616
1859
|
key: `${dir.install_mode}:${dir.name}`,
|
|
1617
1860
|
name: String(manifest?.name || dir.name),
|
|
@@ -1623,10 +1866,43 @@ export class LocalNodeService {
|
|
|
1623
1866
|
manifest_path: existsSync(manifestPath) ? manifestPath : null,
|
|
1624
1867
|
skill_path: existsSync(skillPath) ? skillPath : null,
|
|
1625
1868
|
capabilities: Array.isArray(manifest?.capabilities) ? manifest.capabilities.map((item) => String(item)) : [],
|
|
1869
|
+
owner_dialogue_cheatsheet_path: ownerDialogueCheatsheetPath && existsSync(ownerDialogueCheatsheetPath) ? ownerDialogueCheatsheetPath : null,
|
|
1870
|
+
owner_dialogue_examples_zh: ownerDialogueCheatsheetPath ? readDialogueCheatsheetPreview(ownerDialogueCheatsheetPath) : [],
|
|
1871
|
+
owner_dialogue_sections_zh: ownerDialogueCheatsheetPath ? readDialogueCheatsheetSections(ownerDialogueCheatsheetPath) : [],
|
|
1626
1872
|
bundled_source_path: bundledSkills.find((item) => item.name === String(manifest?.name || dir.name))?.source_path || null,
|
|
1627
1873
|
};
|
|
1628
1874
|
});
|
|
1629
1875
|
|
|
1876
|
+
const installedSkillVersions = new Map(installedSkills.map((item) => [item.name, item.version]));
|
|
1877
|
+
const bundledSkillsWithUpdateState = bundledSkills.map((skill) => {
|
|
1878
|
+
const installedVersion = installedSkillVersions.get(skill.name) || "";
|
|
1879
|
+
const updateAvailable = Boolean(
|
|
1880
|
+
skill.installed_in_openclaw &&
|
|
1881
|
+
installedVersion &&
|
|
1882
|
+
skill.version &&
|
|
1883
|
+
compareVersionTokens(installedVersion, skill.version) < 0
|
|
1884
|
+
);
|
|
1885
|
+
return {
|
|
1886
|
+
...skill,
|
|
1887
|
+
installed_version: installedVersion || null,
|
|
1888
|
+
update_available: updateAvailable,
|
|
1889
|
+
};
|
|
1890
|
+
});
|
|
1891
|
+
const bundledSkillVersions = new Map(bundledSkillsWithUpdateState.map((item) => [item.name, item.version]));
|
|
1892
|
+
const installedSkillsWithUpdateState = installedSkills.map((skill) => {
|
|
1893
|
+
const bundledVersion = bundledSkillVersions.get(skill.name) || "";
|
|
1894
|
+
const updateAvailable = Boolean(
|
|
1895
|
+
bundledVersion &&
|
|
1896
|
+
skill.version &&
|
|
1897
|
+
compareVersionTokens(skill.version, bundledVersion) < 0
|
|
1898
|
+
);
|
|
1899
|
+
return {
|
|
1900
|
+
...skill,
|
|
1901
|
+
bundled_version: bundledVersion || null,
|
|
1902
|
+
update_available: updateAvailable,
|
|
1903
|
+
};
|
|
1904
|
+
});
|
|
1905
|
+
|
|
1630
1906
|
return {
|
|
1631
1907
|
openclaw: {
|
|
1632
1908
|
detected: bridge.openclaw_installation.detected,
|
|
@@ -1637,13 +1913,14 @@ export class LocalNodeService {
|
|
|
1637
1913
|
legacy_install_root: legacyInstallRoot,
|
|
1638
1914
|
},
|
|
1639
1915
|
summary: {
|
|
1640
|
-
bundled_count:
|
|
1641
|
-
installed_count:
|
|
1642
|
-
installed_bundled_count:
|
|
1916
|
+
bundled_count: bundledSkillsWithUpdateState.length,
|
|
1917
|
+
installed_count: installedSkillsWithUpdateState.length,
|
|
1918
|
+
installed_bundled_count: bundledSkillsWithUpdateState.filter((item) => item.installed_in_openclaw).length,
|
|
1919
|
+
update_available_count: bundledSkillsWithUpdateState.filter((item) => item.update_available).length,
|
|
1643
1920
|
},
|
|
1644
1921
|
install_action: bridge.skill_learning.install_action,
|
|
1645
|
-
bundled_skills:
|
|
1646
|
-
installed_skills:
|
|
1922
|
+
bundled_skills: bundledSkillsWithUpdateState,
|
|
1923
|
+
installed_skills: installedSkillsWithUpdateState,
|
|
1647
1924
|
};
|
|
1648
1925
|
}
|
|
1649
1926
|
|
|
@@ -1890,6 +2167,7 @@ export class LocalNodeService {
|
|
|
1890
2167
|
};
|
|
1891
2168
|
const presenceRecord = signPresence(this.identity, Date.now());
|
|
1892
2169
|
const indexRecords = buildIndexRecords(this.profile);
|
|
2170
|
+
const replayMessages = this.getReplayableSelfSocialMessages();
|
|
1893
2171
|
|
|
1894
2172
|
try {
|
|
1895
2173
|
await this.publish("profile", profileRecord);
|
|
@@ -1897,6 +2175,9 @@ export class LocalNodeService {
|
|
|
1897
2175
|
for (const record of indexRecords) {
|
|
1898
2176
|
await this.publish("index", record);
|
|
1899
2177
|
}
|
|
2178
|
+
for (const message of replayMessages) {
|
|
2179
|
+
await this.publish(SOCIAL_MESSAGE_TOPIC, message);
|
|
2180
|
+
}
|
|
1900
2181
|
} catch (error) {
|
|
1901
2182
|
const message = error instanceof Error ? error.message : String(error);
|
|
1902
2183
|
this.lastBroadcastErrorAt = Date.now();
|
|
@@ -1919,7 +2200,10 @@ export class LocalNodeService {
|
|
|
1919
2200
|
this.compactCacheInMemory();
|
|
1920
2201
|
await this.persistCache();
|
|
1921
2202
|
|
|
1922
|
-
await this.log(
|
|
2203
|
+
await this.log(
|
|
2204
|
+
"info",
|
|
2205
|
+
`Broadcast sent (${indexRecords.length} index refs, replayed_messages=${replayMessages.length}, reason=${reason})`
|
|
2206
|
+
);
|
|
1923
2207
|
return { sent: true, reason };
|
|
1924
2208
|
}
|
|
1925
2209
|
|
|
@@ -1939,7 +2223,7 @@ export class LocalNodeService {
|
|
|
1939
2223
|
socialConfig: this.socialConfig,
|
|
1940
2224
|
existingIdentity,
|
|
1941
2225
|
generatedIdentity: createIdentity(),
|
|
1942
|
-
rootDir: this.
|
|
2226
|
+
rootDir: this.projectRoot,
|
|
1943
2227
|
});
|
|
1944
2228
|
this.identity = resolvedIdentity.identity;
|
|
1945
2229
|
this.resolvedIdentitySource = resolvedIdentity.source;
|
|
@@ -1958,7 +2242,7 @@ export class LocalNodeService {
|
|
|
1958
2242
|
socialConfig: this.socialConfig,
|
|
1959
2243
|
agentId: this.identity.agent_id,
|
|
1960
2244
|
existingProfile: existingProfile && existingProfile.agent_id === this.identity.agent_id ? existingProfile : null,
|
|
1961
|
-
rootDir: this.
|
|
2245
|
+
rootDir: this.projectRoot,
|
|
1962
2246
|
});
|
|
1963
2247
|
this.profile = signProfile(profileInput, this.identity);
|
|
1964
2248
|
if (!existingProfile || existingProfile.agent_id !== this.identity.agent_id) {
|
|
@@ -1967,7 +2251,7 @@ export class LocalNodeService {
|
|
|
1967
2251
|
}
|
|
1968
2252
|
await this.profileRepo.set(this.profile);
|
|
1969
2253
|
|
|
1970
|
-
this.directory =
|
|
2254
|
+
this.directory = createEmptyDirectoryState();
|
|
1971
2255
|
this.messageGovernance = {
|
|
1972
2256
|
...this.defaultMessageGovernance(),
|
|
1973
2257
|
...(await this.socialMessageGovernanceRepo.get()),
|
|
@@ -1990,7 +2274,7 @@ export class LocalNodeService {
|
|
|
1990
2274
|
socialConfig: this.socialConfig,
|
|
1991
2275
|
agentId: this.identity.agent_id,
|
|
1992
2276
|
existingProfile: this.profile,
|
|
1993
|
-
rootDir: this.
|
|
2277
|
+
rootDir: this.projectRoot,
|
|
1994
2278
|
});
|
|
1995
2279
|
const nextProfile = signProfile(nextProfileInput, this.identity);
|
|
1996
2280
|
this.profile = nextProfile;
|
|
@@ -2110,6 +2394,10 @@ export class LocalNodeService {
|
|
|
2110
2394
|
await this.log("warn", `Rejected social message with invalid signature (${record.message_id.slice(0, 10)})`);
|
|
2111
2395
|
return;
|
|
2112
2396
|
}
|
|
2397
|
+
if (this.hasSocialMessage(record.message_id)) {
|
|
2398
|
+
await this.publishObservationForMessage(record);
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2113
2401
|
const governanceReason = this.getIncomingSocialMessageRejectionReason(record);
|
|
2114
2402
|
if (governanceReason) {
|
|
2115
2403
|
await this.log("warn", `Rejected social message (${record.message_id.slice(0, 10)}): ${governanceReason}`);
|
|
@@ -2149,7 +2437,7 @@ export class LocalNodeService {
|
|
|
2149
2437
|
clearInterval(this.broadcaster);
|
|
2150
2438
|
}
|
|
2151
2439
|
|
|
2152
|
-
if (!this.broadcastEnabled) {
|
|
2440
|
+
if (!this.broadcastEnabled || !this.networkStarted) {
|
|
2153
2441
|
return;
|
|
2154
2442
|
}
|
|
2155
2443
|
|
|
@@ -2271,6 +2559,7 @@ export class LocalNodeService {
|
|
|
2271
2559
|
}
|
|
2272
2560
|
|
|
2273
2561
|
private async restartNetworkAdapter(reason: string): Promise<void> {
|
|
2562
|
+
this.clearNetworkReconnectTimer();
|
|
2274
2563
|
const previous = this.network;
|
|
2275
2564
|
try {
|
|
2276
2565
|
await previous.stop();
|
|
@@ -2282,16 +2571,63 @@ export class LocalNodeService {
|
|
|
2282
2571
|
this.network = next.adapter;
|
|
2283
2572
|
this.adapterMode = next.mode;
|
|
2284
2573
|
this.networkPort = next.port;
|
|
2285
|
-
|
|
2286
|
-
await this.network.start();
|
|
2574
|
+
this.subscriptionsBound = false;
|
|
2287
2575
|
this.bindNetworkSubscriptions();
|
|
2288
|
-
this.
|
|
2576
|
+
await this.startNetworkAdapterWithRetry(reason);
|
|
2577
|
+
}
|
|
2289
2578
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2579
|
+
private clearNetworkReconnectTimer(): void {
|
|
2580
|
+
if (this.networkReconnectTimer) {
|
|
2581
|
+
clearTimeout(this.networkReconnectTimer);
|
|
2582
|
+
this.networkReconnectTimer = null;
|
|
2292
2583
|
}
|
|
2293
2584
|
}
|
|
2294
2585
|
|
|
2586
|
+
private async startNetworkAdapterWithRetry(reason: string): Promise<void> {
|
|
2587
|
+
this.clearNetworkReconnectTimer();
|
|
2588
|
+
try {
|
|
2589
|
+
await this.network.start();
|
|
2590
|
+
this.networkStarted = true;
|
|
2591
|
+
this.networkStartupError = null;
|
|
2592
|
+
this.networkReconnectDelayMs = 5_000;
|
|
2593
|
+
await this.log(
|
|
2594
|
+
"info",
|
|
2595
|
+
`Local node started (${this.adapterMode}, mode=${this.networkMode}, signaling=${this.webrtcSignalingUrls[0] || "-"}, room=${this.webrtcRoom})`
|
|
2596
|
+
);
|
|
2597
|
+
this.startBroadcastLoop();
|
|
2598
|
+
if (this.broadcastEnabled && this.profile?.public_enabled) {
|
|
2599
|
+
try {
|
|
2600
|
+
await this.broadcastNow(reason);
|
|
2601
|
+
} catch (error) {
|
|
2602
|
+
await this.log(
|
|
2603
|
+
"warn",
|
|
2604
|
+
`Initial broadcast failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2605
|
+
);
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
} catch (error) {
|
|
2609
|
+
this.networkStarted = false;
|
|
2610
|
+
this.networkStartupError = error instanceof Error ? error.message : String(error);
|
|
2611
|
+
await this.log(
|
|
2612
|
+
"warn",
|
|
2613
|
+
`Network start failed (${this.adapterMode}, mode=${this.networkMode}): ${this.networkStartupError}`
|
|
2614
|
+
);
|
|
2615
|
+
this.scheduleNetworkReconnect();
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
private scheduleNetworkReconnect(): void {
|
|
2620
|
+
if (this.networkReconnectTimer) {
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
const delayMs = this.networkReconnectDelayMs;
|
|
2624
|
+
this.networkReconnectTimer = setTimeout(() => {
|
|
2625
|
+
this.networkReconnectTimer = null;
|
|
2626
|
+
void this.startNetworkAdapterWithRetry("adapter_reconnect");
|
|
2627
|
+
}, delayMs);
|
|
2628
|
+
this.networkReconnectDelayMs = Math.min(30_000, Math.max(5_000, Math.floor(delayMs * 1.5)));
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2295
2631
|
private compactCacheInMemory(): number {
|
|
2296
2632
|
const cleaned = cleanupExpiredPresence(this.directory, Date.now(), PRESENCE_TTL_MS);
|
|
2297
2633
|
this.directory = dedupeIndex(cleaned.state);
|
|
@@ -2304,7 +2640,22 @@ export class LocalNodeService {
|
|
|
2304
2640
|
}
|
|
2305
2641
|
|
|
2306
2642
|
private async persistCache(): Promise<void> {
|
|
2307
|
-
|
|
2643
|
+
const persisted = createEmptyDirectoryState();
|
|
2644
|
+
if (this.profile) {
|
|
2645
|
+
const selfProfileRecord: SignedProfileRecord = {
|
|
2646
|
+
type: "profile",
|
|
2647
|
+
profile: this.profile,
|
|
2648
|
+
};
|
|
2649
|
+
this.directory = ingestProfileRecord(this.directory, selfProfileRecord);
|
|
2650
|
+
persisted.profiles[this.profile.agent_id] = this.profile;
|
|
2651
|
+
const selfLastSeenAt = this.directory.presence[this.profile.agent_id];
|
|
2652
|
+
if (typeof selfLastSeenAt === "number" && Number.isFinite(selfLastSeenAt)) {
|
|
2653
|
+
persisted.presence[this.profile.agent_id] = selfLastSeenAt;
|
|
2654
|
+
}
|
|
2655
|
+
const indexed = rebuildIndexForProfile(persisted, this.profile);
|
|
2656
|
+
persisted.index = indexed.index;
|
|
2657
|
+
}
|
|
2658
|
+
await this.cacheRepo.set(persisted);
|
|
2308
2659
|
}
|
|
2309
2660
|
|
|
2310
2661
|
private async persistSocialMessages(): Promise<void> {
|
|
@@ -2330,6 +2681,19 @@ export class LocalNodeService {
|
|
|
2330
2681
|
return (this.network as any).getDiagnostics();
|
|
2331
2682
|
}
|
|
2332
2683
|
|
|
2684
|
+
private getResolvedRealtimeNetworkSummary() {
|
|
2685
|
+
const diagnostics = this.getAdapterDiagnostics();
|
|
2686
|
+
const relayCapable = this.adapterMode === "webrtc-preview" || this.adapterMode === "relay-preview";
|
|
2687
|
+
return {
|
|
2688
|
+
diagnostics,
|
|
2689
|
+
signaling_url: diagnostics?.signaling_url ?? (relayCapable ? this.webrtcSignalingUrls[0] ?? null : null),
|
|
2690
|
+
signaling_endpoints: diagnostics?.signaling_endpoints ?? (relayCapable ? this.webrtcSignalingUrls : []),
|
|
2691
|
+
room: diagnostics?.room ?? (relayCapable ? this.webrtcRoom : null),
|
|
2692
|
+
bootstrap_sources: diagnostics?.bootstrap_sources ?? (relayCapable ? this.webrtcBootstrapSources : []),
|
|
2693
|
+
seed_peers_count: diagnostics?.seed_peers_count ?? this.webrtcSeedPeers.length,
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2333
2697
|
private toPublicProfileSummary(
|
|
2334
2698
|
profile: PublicProfile,
|
|
2335
2699
|
options?: { last_seen_at?: number }
|
|
@@ -2373,6 +2737,72 @@ export class LocalNodeService {
|
|
|
2373
2737
|
});
|
|
2374
2738
|
}
|
|
2375
2739
|
|
|
2740
|
+
private mergeMessageOnlyAgentSummaries(
|
|
2741
|
+
summaries: PublicProfileSummary[],
|
|
2742
|
+
keyword: string
|
|
2743
|
+
): PublicProfileSummary[] {
|
|
2744
|
+
const normalizedKeyword = String(keyword || "").trim().toLowerCase();
|
|
2745
|
+
const knownAgentIds = new Set(summaries.map((item) => item.agent_id));
|
|
2746
|
+
const messageOnly: PublicProfileSummary[] = [];
|
|
2747
|
+
|
|
2748
|
+
for (const message of this.socialMessages) {
|
|
2749
|
+
if (!message?.agent_id || knownAgentIds.has(message.agent_id)) {
|
|
2750
|
+
continue;
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
const displayName = String(message.display_name || "Unnamed").trim() || "Unnamed";
|
|
2754
|
+
if (normalizedKeyword) {
|
|
2755
|
+
const haystacks = [
|
|
2756
|
+
displayName.toLowerCase(),
|
|
2757
|
+
message.agent_id.toLowerCase(),
|
|
2758
|
+
String(message.topic || "").toLowerCase(),
|
|
2759
|
+
];
|
|
2760
|
+
if (!haystacks.some((value) => value.includes(normalizedKeyword))) {
|
|
2761
|
+
continue;
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
knownAgentIds.add(message.agent_id);
|
|
2766
|
+
messageOnly.push(
|
|
2767
|
+
buildPublicProfileSummary({
|
|
2768
|
+
profile: {
|
|
2769
|
+
agent_id: message.agent_id,
|
|
2770
|
+
display_name: displayName,
|
|
2771
|
+
bio: "Seen from signed public message. Profile/presence not synced yet.",
|
|
2772
|
+
tags: ["message-only"],
|
|
2773
|
+
avatar_url: "",
|
|
2774
|
+
public_enabled: true,
|
|
2775
|
+
updated_at: message.created_at,
|
|
2776
|
+
signature: "",
|
|
2777
|
+
},
|
|
2778
|
+
online: false,
|
|
2779
|
+
last_seen_at: null,
|
|
2780
|
+
network_mode: "unknown",
|
|
2781
|
+
openclaw_bound: false,
|
|
2782
|
+
profile_version: PROFILE_VERSION,
|
|
2783
|
+
public_key_fingerprint: null,
|
|
2784
|
+
verified_profile: false,
|
|
2785
|
+
now: Date.now(),
|
|
2786
|
+
presence_ttl_ms: PRESENCE_TTL_MS,
|
|
2787
|
+
})
|
|
2788
|
+
);
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
return [...summaries, ...messageOnly].sort((a, b) => {
|
|
2792
|
+
if (a.online !== b.online) {
|
|
2793
|
+
return a.online ? -1 : 1;
|
|
2794
|
+
}
|
|
2795
|
+
if (a.updated_at !== b.updated_at) {
|
|
2796
|
+
return b.updated_at - a.updated_at;
|
|
2797
|
+
}
|
|
2798
|
+
const byName = a.display_name.localeCompare(b.display_name);
|
|
2799
|
+
if (byName !== 0) {
|
|
2800
|
+
return byName;
|
|
2801
|
+
}
|
|
2802
|
+
return a.agent_id.localeCompare(b.agent_id);
|
|
2803
|
+
});
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2376
2806
|
private fingerprintPublicKey(publicKey: string): string {
|
|
2377
2807
|
const digest = createHash("sha256").update(publicKey, "utf8").digest("hex");
|
|
2378
2808
|
return `${digest.slice(0, 12)}:${digest.slice(-8)}`;
|
|
@@ -2381,6 +2811,22 @@ export class LocalNodeService {
|
|
|
2381
2811
|
private getOnboardingSummary() {
|
|
2382
2812
|
const summary = this.getIntegrationSummary();
|
|
2383
2813
|
const publicEnabled = Boolean(this.profile?.public_enabled);
|
|
2814
|
+
const nextSteps: string[] = [];
|
|
2815
|
+
if (!String(this.profile?.display_name || "").trim()) {
|
|
2816
|
+
nextSteps.push("Update display name in Profile page");
|
|
2817
|
+
}
|
|
2818
|
+
if (!publicEnabled) {
|
|
2819
|
+
nextSteps.push("Enable Public Enabled in Profile");
|
|
2820
|
+
}
|
|
2821
|
+
if (!summary.running) {
|
|
2822
|
+
nextSteps.push("Start broadcast in Network");
|
|
2823
|
+
}
|
|
2824
|
+
if (!summary.discoverable) {
|
|
2825
|
+
nextSteps.push("Announce node once after the network is running");
|
|
2826
|
+
}
|
|
2827
|
+
if (nextSteps.length === 0) {
|
|
2828
|
+
nextSteps.push("Node is public and discoverable");
|
|
2829
|
+
}
|
|
2384
2830
|
return {
|
|
2385
2831
|
first_run: Boolean(
|
|
2386
2832
|
this.initState.social_auto_created ||
|
|
@@ -2392,10 +2838,7 @@ export class LocalNodeService {
|
|
|
2392
2838
|
mode: this.networkMode,
|
|
2393
2839
|
public_enabled: publicEnabled,
|
|
2394
2840
|
can_enable_public_discovery: !publicEnabled,
|
|
2395
|
-
next_steps:
|
|
2396
|
-
"Update display name in Profile page",
|
|
2397
|
-
"Export social.md from Social Config",
|
|
2398
|
-
],
|
|
2841
|
+
next_steps: nextSteps,
|
|
2399
2842
|
};
|
|
2400
2843
|
}
|
|
2401
2844
|
|
|
@@ -2420,7 +2863,7 @@ export class LocalNodeService {
|
|
|
2420
2863
|
};
|
|
2421
2864
|
}
|
|
2422
2865
|
return {
|
|
2423
|
-
mode:
|
|
2866
|
+
mode: DEFAULT_NETWORK_MODE,
|
|
2424
2867
|
short_label: "Relay preview",
|
|
2425
2868
|
summary: "Uses the public relay preview room so public nodes can find each other across the internet.",
|
|
2426
2869
|
};
|
|
@@ -2450,14 +2893,14 @@ export class LocalNodeService {
|
|
|
2450
2893
|
this.socialConfig.network.mode ||
|
|
2451
2894
|
(modeEnv === "local" || modeEnv === "lan" || modeEnv === "global-preview"
|
|
2452
2895
|
? modeEnv
|
|
2453
|
-
:
|
|
2896
|
+
: DEFAULT_NETWORK_MODE);
|
|
2454
2897
|
|
|
2455
2898
|
this.networkMode = resolvedMode;
|
|
2456
|
-
this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE ||
|
|
2457
|
-
this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT ||
|
|
2899
|
+
this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE || DEFAULT_NETWORK_NAMESPACE;
|
|
2900
|
+
this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT || DEFAULT_NETWORK_PORT);
|
|
2458
2901
|
|
|
2459
|
-
const builtInGlobalSignalingUrls = [
|
|
2460
|
-
const builtInGlobalRoom =
|
|
2902
|
+
const builtInGlobalSignalingUrls = [DEFAULT_GLOBAL_SIGNALING_URL];
|
|
2903
|
+
const builtInGlobalRoom = DEFAULT_GLOBAL_ROOM;
|
|
2461
2904
|
|
|
2462
2905
|
const signalingUrlsSocial = dedupeStrings(this.socialConfig.network.signaling_urls || []);
|
|
2463
2906
|
const signalingUrlSocial = String(this.socialConfig.network.signaling_url || "").trim();
|
|
@@ -2482,8 +2925,8 @@ export class LocalNodeService {
|
|
|
2482
2925
|
signalingUrls = builtInGlobalSignalingUrls;
|
|
2483
2926
|
signalingSource = "built-in-defaults:global-preview.signaling_urls";
|
|
2484
2927
|
} else {
|
|
2485
|
-
signalingUrls = [
|
|
2486
|
-
signalingSource =
|
|
2928
|
+
signalingUrls = [DEFAULT_GLOBAL_SIGNALING_URL];
|
|
2929
|
+
signalingSource = `default:${DEFAULT_GLOBAL_SIGNALING_URL}`;
|
|
2487
2930
|
}
|
|
2488
2931
|
|
|
2489
2932
|
const roomSocial = String(this.socialConfig.network.room || "").trim();
|
|
@@ -2492,14 +2935,14 @@ export class LocalNodeService {
|
|
|
2492
2935
|
roomSocial ||
|
|
2493
2936
|
roomEnv ||
|
|
2494
2937
|
(this.networkMode === "global-preview" ? builtInGlobalRoom : "") ||
|
|
2495
|
-
|
|
2938
|
+
DEFAULT_GLOBAL_ROOM;
|
|
2496
2939
|
const roomSource = roomSocial
|
|
2497
2940
|
? "social.md:network.room"
|
|
2498
2941
|
: roomEnv
|
|
2499
2942
|
? "env:WEBRTC_ROOM"
|
|
2500
2943
|
: this.networkMode === "global-preview"
|
|
2501
2944
|
? "built-in-defaults:global-preview.room"
|
|
2502
|
-
:
|
|
2945
|
+
: `default:${DEFAULT_GLOBAL_ROOM}`;
|
|
2503
2946
|
|
|
2504
2947
|
const seedPeersSocial = dedupeStrings(this.socialConfig.network.seed_peers || []);
|
|
2505
2948
|
const seedPeersEnv = dedupeStrings(parseListEnv(WEBRTC_SEED_PEERS));
|
|
@@ -2558,6 +3001,24 @@ export class LocalNodeService {
|
|
|
2558
3001
|
return this.messageGovernance.blocked_terms.some((term) => normalized.includes(term));
|
|
2559
3002
|
}
|
|
2560
3003
|
|
|
3004
|
+
private hasSocialMessage(messageId: string): boolean {
|
|
3005
|
+
return this.socialMessages.some((item) => item.message_id === messageId);
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
private getReplayableSelfSocialMessages(now = Date.now()): SocialMessageRecord[] {
|
|
3009
|
+
const maxCount = Math.max(0, SOCIAL_MESSAGE_REPLAY_MAX_PER_BROADCAST);
|
|
3010
|
+
if (!this.identity || maxCount === 0) {
|
|
3011
|
+
return [];
|
|
3012
|
+
}
|
|
3013
|
+
return this.socialMessages
|
|
3014
|
+
.filter((item) => (
|
|
3015
|
+
item.agent_id === this.identity?.agent_id &&
|
|
3016
|
+
now - item.created_at <= SOCIAL_MESSAGE_REPLAY_WINDOW_MS
|
|
3017
|
+
))
|
|
3018
|
+
.sort((a, b) => a.created_at - b.created_at)
|
|
3019
|
+
.slice(-maxCount);
|
|
3020
|
+
}
|
|
3021
|
+
|
|
2561
3022
|
private hasRecentDuplicateMessage(agentId: string, body: string, topic: string, now = Date.now()): boolean {
|
|
2562
3023
|
return this.socialMessages.some((item) => (
|
|
2563
3024
|
item.agent_id === agentId &&
|
|
@@ -2867,20 +3328,19 @@ function renderBootstrapScript(payload: unknown): string {
|
|
|
2867
3328
|
if (data.pillBroadcastClassName) pillBroadcast.className = data.pillBroadcastClassName;
|
|
2868
3329
|
}
|
|
2869
3330
|
setHtml('overviewCards', data.overviewCardsHtml || '');
|
|
2870
|
-
setText('agentsCountHint', data.agentsCountHintText || '0
|
|
2871
|
-
setHtml('agentsWrap', data.agentsWrapHtml || '<div class="label">No discovered
|
|
3331
|
+
setText('agentsCountHint', data.agentsCountHintText || '0 nodes');
|
|
3332
|
+
setHtml('agentsWrap', data.agentsWrapHtml || '<div class="label">No discovered nodes yet.</div>');
|
|
2872
3333
|
})();
|
|
2873
3334
|
</script>`;
|
|
2874
3335
|
}
|
|
2875
3336
|
|
|
2876
3337
|
export async function main() {
|
|
2877
3338
|
const app = express();
|
|
2878
|
-
const port = Number(process.env.PORT ||
|
|
3339
|
+
const port = Number(process.env.PORT || defaults.ports.local_console);
|
|
2879
3340
|
const staticDir = resolveLocalConsoleStaticDir();
|
|
2880
3341
|
const staticIndexFile = resolve(staticDir, "index.html");
|
|
2881
3342
|
|
|
2882
3343
|
const node = new LocalNodeService();
|
|
2883
|
-
await node.start();
|
|
2884
3344
|
|
|
2885
3345
|
app.use(cors({ origin: true }));
|
|
2886
3346
|
app.use(express.json());
|
|
@@ -3083,9 +3543,10 @@ export async function main() {
|
|
|
3083
3543
|
|
|
3084
3544
|
app.post(
|
|
3085
3545
|
"/api/openclaw/bridge/skill-install",
|
|
3086
|
-
asyncRoute(async (
|
|
3546
|
+
asyncRoute(async (req, res) => {
|
|
3087
3547
|
try {
|
|
3088
|
-
const
|
|
3548
|
+
const skillName = String(req.body?.skill_name || "").trim();
|
|
3549
|
+
const result = await node.installOpenClawSkill(skillName || undefined);
|
|
3089
3550
|
sendOk(res, result, {
|
|
3090
3551
|
message: "OpenClaw skill installed",
|
|
3091
3552
|
});
|
|
@@ -3133,7 +3594,7 @@ export async function main() {
|
|
|
3133
3594
|
const agentId = req.params.agentId;
|
|
3134
3595
|
const profile = state.profiles[agentId];
|
|
3135
3596
|
if (!profile) {
|
|
3136
|
-
sendError(res, 404, "AGENT_NOT_FOUND", "
|
|
3597
|
+
sendError(res, 404, "AGENT_NOT_FOUND", "Node not found", { agent_id: agentId });
|
|
3137
3598
|
return;
|
|
3138
3599
|
}
|
|
3139
3600
|
|
|
@@ -3169,7 +3630,7 @@ export async function main() {
|
|
|
3169
3630
|
.join("");
|
|
3170
3631
|
const agentsWrapHtml =
|
|
3171
3632
|
discovered.length === 0
|
|
3172
|
-
? `<div class="label">No discovered
|
|
3633
|
+
? `<div class="label">No discovered nodes yet.</div>`
|
|
3173
3634
|
: `
|
|
3174
3635
|
<table class="table">
|
|
3175
3636
|
<thead><tr><th>Name</th><th>Agent ID</th><th>Status</th><th>Updated</th></tr></thead>
|
|
@@ -3205,7 +3666,7 @@ export async function main() {
|
|
|
3205
3666
|
pillBroadcastText: overview.broadcast_enabled ? "broadcast: running" : "broadcast: paused",
|
|
3206
3667
|
pillBroadcastClassName: `pill ${overview.broadcast_enabled ? "ok" : "warn"}`,
|
|
3207
3668
|
overviewCardsHtml,
|
|
3208
|
-
agentsCountHintText: `${discovered.length}
|
|
3669
|
+
agentsCountHintText: `${discovered.length} nodes discovered`,
|
|
3209
3670
|
agentsWrapHtml,
|
|
3210
3671
|
integrationStatusText: `Connected to SilicaClaw: ${integration.connected_to_silicaclaw ? "yes" : "no"} · Network mode: ${integration.network_mode || "-"} · Public discovery: ${integration.public_enabled ? "enabled" : "disabled"}`,
|
|
3211
3672
|
integrationStatusClassName: `integration-strip ${integration.connected_to_silicaclaw && integration.public_enabled ? "ok" : "warn"}`,
|
|
@@ -3230,6 +3691,8 @@ export async function main() {
|
|
|
3230
3691
|
console.log(`SilicaClaw local-console running: http://localhost:${port}`);
|
|
3231
3692
|
});
|
|
3232
3693
|
|
|
3694
|
+
await node.start();
|
|
3695
|
+
|
|
3233
3696
|
process.on("SIGINT", async () => {
|
|
3234
3697
|
await node.stop();
|
|
3235
3698
|
process.exit(0);
|