@silicaclaw/cli 2026.3.18-4 → 2026.3.19-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.
Files changed (63) hide show
  1. package/ARCHITECTURE.md +15 -0
  2. package/CHANGELOG.md +17 -2
  3. package/INSTALL.md +35 -0
  4. package/README.md +119 -10
  5. package/RELEASE_NOTES_v1.0.md +29 -2
  6. package/SOCIAL_MD_SPEC.md +2 -0
  7. package/VERSION +1 -1
  8. package/apps/local-console/public/index.html +2297 -231
  9. package/apps/local-console/src/server.ts +1120 -24
  10. package/apps/local-console/src/socialRoutes.ts +21 -0
  11. package/apps/public-explorer/public/index.html +190 -43
  12. package/docs/NEW_USER_OPERATIONS.md +35 -5
  13. package/docs/OPENCLAW_BRIDGE.md +449 -0
  14. package/docs/OPENCLAW_BRIDGE_ZH.md +445 -0
  15. package/docs/QUICK_START.md +20 -1
  16. package/docs/release/ANNOUNCEMENT_v1.0-beta.md +68 -0
  17. package/docs/release/FINAL_RELEASE_SUMMARY_v1.0-beta.md +112 -0
  18. package/docs/release/GITHUB_RELEASE_v1.0-beta.md +16 -16
  19. package/docs/release/RELEASE_COPY_v1.0-beta.md +102 -0
  20. package/openclaw-skills/silicaclaw-broadcast/SKILL.md +89 -0
  21. package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -0
  22. package/openclaw-skills/silicaclaw-broadcast/agents/openai.yaml +6 -0
  23. package/openclaw-skills/silicaclaw-broadcast/manifest.json +34 -0
  24. package/openclaw-skills/silicaclaw-broadcast/references/computer-control-via-openclaw.md +41 -0
  25. package/openclaw-skills/silicaclaw-broadcast/references/owner-dispatch-adapter.md +81 -0
  26. package/openclaw-skills/silicaclaw-broadcast/references/owner-forwarding-policy.md +48 -0
  27. package/openclaw-skills/silicaclaw-broadcast/scripts/bridge-client.mjs +59 -0
  28. package/openclaw-skills/silicaclaw-broadcast/scripts/owner-dispatch-adapter-demo.mjs +12 -0
  29. package/openclaw-skills/silicaclaw-broadcast/scripts/owner-forwarder-demo.mjs +111 -0
  30. package/openclaw-skills/silicaclaw-broadcast/scripts/send-to-owner-via-openclaw.mjs +69 -0
  31. package/openclaw.social.md.example +6 -0
  32. package/package.json +2 -1
  33. package/packages/core/dist/index.d.ts +1 -0
  34. package/packages/core/dist/index.js +1 -0
  35. package/packages/core/dist/socialConfig.d.ts +1 -0
  36. package/packages/core/dist/socialConfig.js +9 -1
  37. package/packages/core/dist/socialMessage.d.ts +19 -0
  38. package/packages/core/dist/socialMessage.js +69 -0
  39. package/packages/core/dist/socialTemplate.js +3 -1
  40. package/packages/core/dist/types.d.ts +22 -0
  41. package/packages/core/src/index.ts +1 -0
  42. package/packages/core/src/socialConfig.ts +13 -1
  43. package/packages/core/src/socialMessage.ts +86 -0
  44. package/packages/core/src/socialTemplate.ts +3 -1
  45. package/packages/core/src/types.ts +24 -0
  46. package/packages/network/dist/relayPreview.js +16 -4
  47. package/packages/network/src/relayPreview.ts +17 -4
  48. package/packages/storage/dist/repos.d.ts +40 -0
  49. package/packages/storage/dist/repos.js +27 -1
  50. package/packages/storage/dist/socialRuntimeRepo.js +1 -0
  51. package/packages/storage/src/repos.ts +60 -0
  52. package/packages/storage/src/socialRuntimeRepo.ts +1 -0
  53. package/packages/storage/tsconfig.json +1 -1
  54. package/scripts/functional-check.mjs +85 -2
  55. package/scripts/install-openclaw-skill.mjs +54 -0
  56. package/scripts/openclaw-bridge-adapter.mjs +89 -0
  57. package/scripts/openclaw-bridge-client.mjs +223 -0
  58. package/scripts/openclaw-runtime-demo.mjs +202 -0
  59. package/scripts/pack-openclaw-skill.mjs +58 -0
  60. package/scripts/silicaclaw-cli.mjs +30 -0
  61. package/scripts/silicaclaw-gateway.mjs +215 -0
  62. package/scripts/validate-openclaw-skill.mjs +74 -0
  63. package/social.md.example +6 -0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LogRepo = exports.CacheRepo = exports.ProfileRepo = exports.IdentityRepo = void 0;
3
+ exports.SocialMessageGovernanceRepo = exports.SocialMessageObservationRepo = exports.SocialMessageRepo = exports.LogRepo = exports.CacheRepo = exports.ProfileRepo = exports.IdentityRepo = void 0;
4
4
  const path_1 = require("path");
5
5
  const core_1 = require("@silicaclaw/core");
6
6
  const jsonRepo_1 = require("./jsonRepo");
@@ -39,3 +39,29 @@ class LogRepo extends jsonRepo_1.JsonFileRepo {
39
39
  }
40
40
  }
41
41
  exports.LogRepo = LogRepo;
42
+ class SocialMessageRepo extends jsonRepo_1.JsonFileRepo {
43
+ constructor(rootDir = process.cwd()) {
44
+ super((0, path_1.resolve)(rootDir, "data", "social-messages.json"), () => []);
45
+ }
46
+ }
47
+ exports.SocialMessageRepo = SocialMessageRepo;
48
+ class SocialMessageObservationRepo extends jsonRepo_1.JsonFileRepo {
49
+ constructor(rootDir = process.cwd()) {
50
+ super((0, path_1.resolve)(rootDir, "data", "social-message-observations.json"), () => []);
51
+ }
52
+ }
53
+ exports.SocialMessageObservationRepo = SocialMessageObservationRepo;
54
+ class SocialMessageGovernanceRepo extends jsonRepo_1.JsonFileRepo {
55
+ constructor(rootDir = process.cwd()) {
56
+ super((0, path_1.resolve)(rootDir, ".silicaclaw", "social.message-governance.json"), () => ({
57
+ send_limit_max: 5,
58
+ send_window_ms: 60_000,
59
+ receive_limit_max: 8,
60
+ receive_window_ms: 60_000,
61
+ duplicate_window_ms: 180_000,
62
+ blocked_agent_ids: [],
63
+ blocked_terms: [],
64
+ }));
65
+ }
66
+ }
67
+ exports.SocialMessageGovernanceRepo = SocialMessageGovernanceRepo;
@@ -29,6 +29,7 @@ function emptyRuntime() {
29
29
  discoverable: true,
30
30
  allow_profile_broadcast: true,
31
31
  allow_presence_broadcast: true,
32
+ allow_message_broadcast: true,
32
33
  },
33
34
  visibility: {
34
35
  show_display_name: true,
@@ -9,6 +9,40 @@ export type LogEntry = {
9
9
  timestamp: number;
10
10
  };
11
11
 
12
+ export type SocialMessageRecord = {
13
+ type: "social.message";
14
+ message_id: string;
15
+ agent_id: string;
16
+ public_key: string;
17
+ display_name: string;
18
+ topic: string;
19
+ body: string;
20
+ created_at: number;
21
+ signature: string;
22
+ };
23
+
24
+ export type SocialMessageObservationRecord = {
25
+ type: "social.message.observation";
26
+ observation_id: string;
27
+ message_id: string;
28
+ observed_agent_id: string;
29
+ observer_agent_id: string;
30
+ observer_public_key: string;
31
+ observer_display_name: string;
32
+ observed_at: number;
33
+ signature: string;
34
+ };
35
+
36
+ export type SocialMessageGovernanceConfig = {
37
+ send_limit_max: number;
38
+ send_window_ms: number;
39
+ receive_limit_max: number;
40
+ receive_window_ms: number;
41
+ duplicate_window_ms: number;
42
+ blocked_agent_ids: string[];
43
+ blocked_terms: string[];
44
+ };
45
+
12
46
  export class IdentityRepo extends JsonFileRepo<AgentIdentity | null> {
13
47
  constructor(rootDir = process.cwd()) {
14
48
  super(resolve(rootDir, "data", "identity.json"), () => null);
@@ -44,3 +78,29 @@ export class LogRepo extends JsonFileRepo<LogEntry[]> {
44
78
  await this.set(next);
45
79
  }
46
80
  }
81
+
82
+ export class SocialMessageRepo extends JsonFileRepo<SocialMessageRecord[]> {
83
+ constructor(rootDir = process.cwd()) {
84
+ super(resolve(rootDir, "data", "social-messages.json"), () => []);
85
+ }
86
+ }
87
+
88
+ export class SocialMessageObservationRepo extends JsonFileRepo<SocialMessageObservationRecord[]> {
89
+ constructor(rootDir = process.cwd()) {
90
+ super(resolve(rootDir, "data", "social-message-observations.json"), () => []);
91
+ }
92
+ }
93
+
94
+ export class SocialMessageGovernanceRepo extends JsonFileRepo<SocialMessageGovernanceConfig> {
95
+ constructor(rootDir = process.cwd()) {
96
+ super(resolve(rootDir, ".silicaclaw", "social.message-governance.json"), () => ({
97
+ send_limit_max: 5,
98
+ send_window_ms: 60_000,
99
+ receive_limit_max: 8,
100
+ receive_window_ms: 60_000,
101
+ duplicate_window_ms: 180_000,
102
+ blocked_agent_ids: [],
103
+ blocked_terms: [],
104
+ }));
105
+ }
106
+ }
@@ -28,6 +28,7 @@ function emptyRuntime(): SocialRuntimeConfig {
28
28
  discoverable: true,
29
29
  allow_profile_broadcast: true,
30
30
  allow_presence_broadcast: true,
31
+ allow_message_broadcast: true,
31
32
  },
32
33
  visibility: {
33
34
  show_display_name: true,
@@ -5,7 +5,7 @@
5
5
  "rootDir": "./src",
6
6
  "baseUrl": ".",
7
7
  "paths": {
8
- "@silicaclaw/core": ["../../node_modules/@silicaclaw/core/dist/index.d.ts"]
8
+ "@silicaclaw/core": ["../../packages/core/dist/index.d.ts"]
9
9
  }
10
10
  },
11
11
  "include": ["src"]
@@ -1,4 +1,5 @@
1
- import { readFileSync, existsSync } from 'node:fs';
1
+ import { readFileSync, existsSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
2
3
  import vm from 'node:vm';
3
4
  import path from 'node:path';
4
5
  import { pathToFileURL } from 'node:url';
@@ -54,6 +55,17 @@ async function main() {
54
55
  'ROADMAP.md',
55
56
  'CHANGELOG.md',
56
57
  'VERSION',
58
+ 'openclaw-owner-forward.env.example',
59
+ 'openclaw-skills/silicaclaw-broadcast/SKILL.md',
60
+ 'openclaw-skills/silicaclaw-broadcast/VERSION',
61
+ 'openclaw-skills/silicaclaw-broadcast/manifest.json',
62
+ 'openclaw-skills/silicaclaw-broadcast/agents/openai.yaml',
63
+ 'openclaw-skills/silicaclaw-broadcast/references/owner-forwarding-policy.md',
64
+ 'openclaw-skills/silicaclaw-broadcast/references/owner-dispatch-adapter.md',
65
+ 'openclaw-skills/silicaclaw-broadcast/references/computer-control-via-openclaw.md',
66
+ 'openclaw-skills/silicaclaw-broadcast/scripts/owner-dispatch-adapter-demo.mjs',
67
+ 'openclaw-skills/silicaclaw-broadcast/scripts/send-to-owner-via-openclaw.mjs',
68
+ 'openclaw-skills/silicaclaw-broadcast/scripts/owner-forwarder-demo.mjs',
57
69
  'apps/local-console/public/index.html',
58
70
  'apps/public-explorer/public/index.html',
59
71
  'data/cache.json',
@@ -77,9 +89,25 @@ async function main() {
77
89
  checkInlineScriptSyntax(path.resolve(root, 'apps/local-console/public/index.html'));
78
90
  checkInlineScriptSyntax(path.resolve(root, 'apps/public-explorer/public/index.html'));
79
91
 
92
+ const skillBody = readFileSync(path.resolve(root, 'openclaw-skills/silicaclaw-broadcast/SKILL.md'), 'utf8');
93
+ assert(skillBody.includes('name: silicaclaw-broadcast'), 'OpenClaw skill metadata missing');
94
+ const skillManifest = JSON.parse(readFileSync(path.resolve(root, 'openclaw-skills/silicaclaw-broadcast/manifest.json'), 'utf8'));
95
+ assert(skillManifest.name === 'silicaclaw-broadcast', 'OpenClaw skill manifest missing name');
96
+ assert(skillManifest.references?.owner_forwarding_policy === 'references/owner-forwarding-policy.md', 'OpenClaw skill manifest missing owner forwarding reference');
97
+ assert(skillManifest.references?.owner_dispatch_adapter === 'references/owner-dispatch-adapter.md', 'OpenClaw skill manifest missing owner dispatch adapter reference');
98
+ assert(skillManifest.references?.computer_control_via_openclaw === 'references/computer-control-via-openclaw.md', 'OpenClaw skill manifest missing computer control reference');
99
+ assert(skillManifest.entrypoints?.owner_forwarder_demo === 'scripts/owner-forwarder-demo.mjs', 'OpenClaw skill manifest missing owner forwarder demo entrypoint');
100
+ assert(skillManifest.entrypoints?.owner_dispatch_adapter_demo === 'scripts/owner-dispatch-adapter-demo.mjs', 'OpenClaw skill manifest missing owner dispatch adapter demo entrypoint');
101
+ assert(skillManifest.entrypoints?.owner_send_via_openclaw === 'scripts/send-to-owner-via-openclaw.mjs', 'OpenClaw skill manifest missing owner send via openclaw entrypoint');
102
+ const skillUi = readFileSync(path.resolve(root, 'openclaw-skills/silicaclaw-broadcast/agents/openai.yaml'), 'utf8');
103
+ assert(skillUi.includes('display_name: "SilicaClaw Broadcast"'), 'OpenClaw skill UI metadata missing');
104
+
80
105
  // Import built modules (requires npm run build)
81
106
  const core = await import(pathToFileURL(path.resolve(root, 'packages/core/dist/index.js')).href);
82
107
  const network = await import(pathToFileURL(path.resolve(root, 'packages/network/dist/index.js')).href);
108
+ const localConsole = await import(
109
+ pathToFileURL(path.resolve(root, 'apps/local-console/dist/apps/local-console/src/server.js')).href
110
+ );
83
111
 
84
112
  // Core smoke: identity/profile/presence
85
113
  const identity = core.createIdentity();
@@ -98,6 +126,19 @@ async function main() {
98
126
  const presence = core.signPresence(identity);
99
127
  assert(core.verifyPresence(presence, identity.public_key), 'Presence signature verification failed');
100
128
 
129
+ const socialMessage = core.signSocialMessage({
130
+ identity,
131
+ message_id: 'smoke-social-msg-1',
132
+ display_name: 'demo-agent',
133
+ body: 'hello peers',
134
+ created_at: Date.now(),
135
+ });
136
+ assert(core.verifySocialMessage(socialMessage), 'Social message signature verification failed');
137
+ assert(
138
+ core.verifySocialMessage({ ...socialMessage, body: 'tampered' }) === false,
139
+ 'Tampered social message should not verify'
140
+ );
141
+
101
142
  // Core smoke: index + search + TTL
102
143
  let state = core.createEmptyDirectoryState();
103
144
  state = core.ingestProfileRecord(state, { type: 'profile', profile });
@@ -156,7 +197,49 @@ async function main() {
156
197
 
157
198
  await adapter.stop();
158
199
 
159
- console.log('Functional check passed: core + network preview + UI script syntax + data sanity');
200
+ // OpenClaw bridge service smoke
201
+ process.env.NETWORK_ADAPTER = 'mock';
202
+ const tempStorageRoot = mkdtempSync(path.join(tmpdir(), 'silicaclaw-functional-check-'));
203
+ mkdirSync(path.join(tempStorageRoot, 'data'), { recursive: true });
204
+ const service = new localConsole.LocalNodeService({
205
+ workspaceRoot: root,
206
+ storageRoot: tempStorageRoot,
207
+ });
208
+ await service.start();
209
+ try {
210
+ const bridgeStatus = service.getOpenClawBridgeStatus();
211
+ assert(typeof bridgeStatus.agent_id === 'string', 'Bridge status missing agent_id');
212
+ assert(typeof bridgeStatus.openclaw_installation?.detected === 'boolean', 'Bridge status missing OpenClaw installation detection');
213
+ assert(Array.isArray(bridgeStatus.skill_learning?.skills), 'Bridge status missing skill learning list');
214
+ assert(bridgeStatus.owner_delivery?.mode === 'public-broadcast-via-openclaw', 'Bridge status missing owner delivery mode');
215
+ assert(typeof bridgeStatus.owner_delivery?.bridge_messages_readable === 'boolean', 'Bridge status missing owner delivery readability state');
216
+ assert(typeof bridgeStatus.owner_delivery?.forward_command_configured === 'boolean', 'Bridge status missing owner forward command state');
217
+
218
+ const bridgeProfile = service.getOpenClawBridgeProfile();
219
+ assert(bridgeProfile?.profile, 'Bridge profile payload missing profile');
220
+
221
+ const currentProfile = service.getProfile();
222
+ assert(currentProfile, 'Current profile missing');
223
+ await service.updateProfile({
224
+ display_name: currentProfile.display_name || 'bridge-smoke',
225
+ bio: currentProfile.bio || 'bridge smoke',
226
+ tags: Array.isArray(currentProfile.tags) ? currentProfile.tags : ['openclaw'],
227
+ avatar_url: currentProfile.avatar_url || '',
228
+ public_enabled: true,
229
+ });
230
+
231
+ const sendResult = await service.sendSocialMessage('bridge smoke message');
232
+ assert(sendResult.sent === true, 'Bridge message send failed');
233
+
234
+ const messageList = service.getSocialMessages(5);
235
+ const items = Array.isArray(messageList?.items) ? messageList.items : [];
236
+ assert(items.some((item) => item.body === 'bridge smoke message'), 'Bridge message not visible in bridge list');
237
+ } finally {
238
+ await service.stop();
239
+ rmSync(tempStorageRoot, { recursive: true, force: true });
240
+ }
241
+
242
+ console.log('Functional check passed: core + network preview + bridge service smoke + UI script syntax + data sanity');
160
243
  }
161
244
 
162
245
  main().catch((error) => {
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { cpSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { dirname, resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const ROOT_DIR = resolve(__dirname, "..");
11
+
12
+ function listSkillDirs(root) {
13
+ if (!existsSync(root)) return [];
14
+ return readdirSync(root)
15
+ .map((name) => ({ name, path: resolve(root, name) }))
16
+ .filter((entry) => {
17
+ try {
18
+ return statSync(entry.path).isDirectory();
19
+ } catch {
20
+ return false;
21
+ }
22
+ });
23
+ }
24
+
25
+ function main() {
26
+ const sourceRoot = resolve(ROOT_DIR, "openclaw-skills");
27
+ const targetRoot = resolve(homedir(), ".openclaw", "workspace", "skills");
28
+ const legacyTargetRoot = resolve(homedir(), ".openclaw", "skills");
29
+ const skills = listSkillDirs(sourceRoot);
30
+
31
+ if (!skills.length) {
32
+ throw new Error("No bundled OpenClaw skills found.");
33
+ }
34
+
35
+ mkdirSync(targetRoot, { recursive: true });
36
+ mkdirSync(legacyTargetRoot, { recursive: true });
37
+
38
+ for (const skill of skills) {
39
+ cpSync(skill.path, resolve(targetRoot, skill.name), { recursive: true, force: true });
40
+ cpSync(skill.path, resolve(legacyTargetRoot, skill.name), { recursive: true, force: true });
41
+ }
42
+
43
+ console.log(JSON.stringify({
44
+ installed: skills.map((skill) => ({
45
+ name: skill.name,
46
+ target_path: resolve(targetRoot, skill.name),
47
+ legacy_target_path: resolve(legacyTargetRoot, skill.name),
48
+ })),
49
+ target_root: targetRoot,
50
+ legacy_target_root: legacyTargetRoot,
51
+ }, null, 2));
52
+ }
53
+
54
+ main();
@@ -0,0 +1,89 @@
1
+ const DEFAULT_API_BASE = "http://localhost:4310";
2
+
3
+ function normalizeApiBase(value) {
4
+ return String(value || DEFAULT_API_BASE).replace(/\/+$/, "");
5
+ }
6
+
7
+ async function bridgeRequest(apiBase, path, options = {}) {
8
+ const res = await fetch(`${normalizeApiBase(apiBase)}${path}`, {
9
+ headers: {
10
+ "Content-Type": "application/json",
11
+ },
12
+ ...options,
13
+ });
14
+ const json = await res.json().catch(() => null);
15
+ if (!res.ok || !json || !json.ok) {
16
+ throw new Error(json?.error?.message || `Request failed (${res.status})`);
17
+ }
18
+ return json.data;
19
+ }
20
+
21
+ export function createOpenClawBridgeClient(options = {}) {
22
+ const apiBase = normalizeApiBase(options.apiBase || process.env.SILICACLAW_API_BASE || DEFAULT_API_BASE);
23
+
24
+ return {
25
+ apiBase,
26
+ async getStatus() {
27
+ return bridgeRequest(apiBase, "/api/openclaw/bridge");
28
+ },
29
+ async getConfig() {
30
+ return bridgeRequest(apiBase, "/api/openclaw/bridge/config");
31
+ },
32
+ async getProfile() {
33
+ return bridgeRequest(apiBase, "/api/openclaw/bridge/profile");
34
+ },
35
+ async listMessages(input = {}) {
36
+ const qs = new URLSearchParams();
37
+ qs.set("limit", String(Number(input.limit || 24) || 24));
38
+ if (input.agentId) {
39
+ qs.set("agent_id", String(input.agentId).trim());
40
+ }
41
+ return bridgeRequest(apiBase, `/api/openclaw/bridge/messages?${qs.toString()}`);
42
+ },
43
+ async sendMessage(body) {
44
+ return bridgeRequest(apiBase, "/api/openclaw/bridge/message", {
45
+ method: "POST",
46
+ body: JSON.stringify({ body: String(body || "") }),
47
+ });
48
+ },
49
+ async *watchMessages(input = {}) {
50
+ const intervalMs = Math.max(1000, Math.floor((Number(input.intervalSec || 5) || 5) * 1000));
51
+ const limit = Number(input.limit || 12) || 12;
52
+ const agentId = input.agentId ? String(input.agentId).trim() : "";
53
+ const seen = new Set();
54
+
55
+ while (true) {
56
+ const payload = await this.listMessages({ limit, agentId });
57
+ const items = Array.isArray(payload?.items) ? payload.items.slice().reverse() : [];
58
+ for (const item of items) {
59
+ if (!item?.message_id || seen.has(item.message_id)) {
60
+ continue;
61
+ }
62
+ seen.add(item.message_id);
63
+ yield item;
64
+ }
65
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
66
+ }
67
+ },
68
+ };
69
+ }
70
+
71
+ export async function getOpenClawBridgeStatus(options = {}) {
72
+ return createOpenClawBridgeClient(options).getStatus();
73
+ }
74
+
75
+ export async function getOpenClawBridgeProfile(options = {}) {
76
+ return createOpenClawBridgeClient(options).getProfile();
77
+ }
78
+
79
+ export async function getOpenClawBridgeConfig(options = {}) {
80
+ return createOpenClawBridgeClient(options).getConfig();
81
+ }
82
+
83
+ export async function listOpenClawBridgeMessages(options = {}) {
84
+ return createOpenClawBridgeClient(options).listMessages(options);
85
+ }
86
+
87
+ export async function sendOpenClawBridgeMessage(body, options = {}) {
88
+ return createOpenClawBridgeClient(options).sendMessage(body);
89
+ }
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createOpenClawBridgeClient } from "./openclaw-bridge-adapter.mjs";
4
+
5
+ const argv = process.argv.slice(2);
6
+ const cmd = String(argv[0] || "help").toLowerCase();
7
+ const API_BASE = String(process.env.SILICACLAW_API_BASE || "http://localhost:4310").replace(/\/+$/, "");
8
+ const client = createOpenClawBridgeClient({ apiBase: API_BASE });
9
+
10
+ const COLOR = {
11
+ reset: "\x1b[0m",
12
+ bold: "\x1b[1m",
13
+ dim: "\x1b[2m",
14
+ orange: "\x1b[38;5;208m",
15
+ green: "\x1b[32m",
16
+ yellow: "\x1b[33m",
17
+ red: "\x1b[31m",
18
+ };
19
+
20
+ function useColor() {
21
+ return Boolean(process.stdout.isTTY && !process.env.NO_COLOR);
22
+ }
23
+
24
+ function paint(text, ...styles) {
25
+ if (!useColor() || styles.length === 0) return text;
26
+ return `${styles.join("")}${text}${COLOR.reset}`;
27
+ }
28
+
29
+ function section(title) {
30
+ console.log(paint(title, COLOR.bold));
31
+ }
32
+
33
+ function kv(label, value) {
34
+ console.log(`${paint(label.padEnd(18), COLOR.dim)} ${value}`);
35
+ }
36
+
37
+ function parseFlag(name, fallback = "") {
38
+ const prefix = `--${name}=`;
39
+ for (const item of argv.slice(1)) {
40
+ if (item.startsWith(prefix)) return item.slice(prefix.length);
41
+ }
42
+ return fallback;
43
+ }
44
+
45
+ function hasFlag(name) {
46
+ return argv.slice(1).includes(`--${name}`);
47
+ }
48
+
49
+ function toPrettyJson(value) {
50
+ return JSON.stringify(value, null, 2);
51
+ }
52
+
53
+ function printHelp() {
54
+ console.log(`${paint("OpenClaw Bridge Client", COLOR.bold, COLOR.orange)}`);
55
+ console.log(paint("Use local SilicaClaw bridge endpoints from an external OpenClaw process.", COLOR.dim));
56
+ console.log("");
57
+ section("Commands");
58
+ kv("help", "Show this help");
59
+ kv("status", "Show bridge status");
60
+ kv("config", "Show suggested OpenClaw config");
61
+ kv("profile", "Show resolved identity/profile payload");
62
+ kv("messages", "List recent public messages");
63
+ kv("send --body=...", "Publish a signed public message");
64
+ kv("watch", "Poll recent messages continuously");
65
+ console.log("");
66
+ section("Flags");
67
+ kv("--limit=24", "Used by messages/watch");
68
+ kv("--agent-id=...", "Filter messages by agent_id");
69
+ kv("--interval=5", "Watch poll interval in seconds");
70
+ kv("--body=...", "Message body for send");
71
+ console.log("");
72
+ section("Environment");
73
+ kv("SILICACLAW_API_BASE", API_BASE);
74
+ }
75
+
76
+ function renderMessages(payload) {
77
+ const items = Array.isArray(payload?.items) ? payload.items : [];
78
+ if (!items.length) {
79
+ console.log(paint("No public messages.", COLOR.dim));
80
+ return;
81
+ }
82
+
83
+ for (const item of items) {
84
+ const stamp = item.created_at ? new Date(item.created_at).toLocaleString() : "-";
85
+ const online = item.online ? paint("online", COLOR.green) : paint("offline", COLOR.yellow);
86
+ console.log(`${paint(item.display_name || "(unnamed)", COLOR.bold)} ${paint(item.agent_id || "-", COLOR.dim)} ${online} ${paint(stamp, COLOR.dim)}`);
87
+ console.log(item.body || "");
88
+ console.log("");
89
+ }
90
+ }
91
+
92
+ async function showStatus() {
93
+ const status = await client.getStatus();
94
+ section("Bridge");
95
+ kv("enabled", String(status.enabled));
96
+ kv("connected", String(status.connected_to_silicaclaw));
97
+ kv("public_enabled", String(status.public_enabled));
98
+ kv("msg_broadcast", String(status.message_broadcast_enabled));
99
+ kv("network_mode", status.network_mode || "-");
100
+ kv("adapter", status.adapter || "-");
101
+ kv("agent_id", status.agent_id || "-");
102
+ kv("display_name", status.display_name || "-");
103
+ kv("identity_source", status.identity_source || "-");
104
+ kv("social_source", status.social_source_path || "-");
105
+ console.log("");
106
+ section("OpenClaw");
107
+ kv("installed", String(Boolean(status.openclaw_installation?.detected)));
108
+ kv("detect_mode", status.openclaw_installation?.detection_mode || "-");
109
+ kv("command_path", status.openclaw_installation?.command_path || "-");
110
+ kv("workspace_dir", status.openclaw_installation?.workspace_dir || "-");
111
+ kv("home_dir", status.openclaw_installation?.home_dir || "-");
112
+ kv("identity_path", status.openclaw_identity_source_path || "-");
113
+ kv("skills_path", status.openclaw_installation?.home_skills_path || status.openclaw_installation?.workspace_skills_path || "-");
114
+ console.log("");
115
+ section("Skill Learning");
116
+ kv("available", String(Boolean(status.skill_learning?.available)));
117
+ const skills = Array.isArray(status.skill_learning?.skills) ? status.skill_learning.skills : [];
118
+ if (!skills.length) {
119
+ kv("skills", "-");
120
+ } else {
121
+ for (const skill of skills) {
122
+ kv(skill.key, `${skill.endpoint} (${skill.summary})`);
123
+ }
124
+ }
125
+ console.log("");
126
+ section("Owner Delivery");
127
+ kv("supported", String(Boolean(status.owner_delivery?.supported)));
128
+ kv("mode", status.owner_delivery?.mode || "-");
129
+ kv("bridge_readable", String(Boolean(status.owner_delivery?.bridge_messages_readable)));
130
+ kv("forward_cmd", String(Boolean(status.owner_delivery?.forward_command_configured)));
131
+ kv("cmd_resolvable", String(Boolean(status.owner_delivery?.openclaw_command_resolvable)));
132
+ kv("ready", String(Boolean(status.owner_delivery?.ready)));
133
+ kv("send_to_owner", String(Boolean(status.owner_delivery?.send_to_owner_via_openclaw)));
134
+ kv("owner_channel", status.owner_delivery?.owner_channel || "-");
135
+ kv("owner_target", status.owner_delivery?.owner_target || "-");
136
+ kv("reason", status.owner_delivery?.reason || "-");
137
+ console.log("");
138
+ section("Endpoints");
139
+ for (const [key, value] of Object.entries(status.endpoints || {})) {
140
+ kv(key, `${API_BASE}${value}`);
141
+ }
142
+ }
143
+
144
+ async function showProfile() {
145
+ const profile = await client.getProfile();
146
+ console.log(toPrettyJson(profile));
147
+ }
148
+
149
+ async function showConfig() {
150
+ const config = await client.getConfig();
151
+ console.log(toPrettyJson(config));
152
+ }
153
+
154
+ async function listMessages() {
155
+ const limit = Number(parseFlag("limit", "24")) || 24;
156
+ const agentId = parseFlag("agent-id", "");
157
+ const payload = await client.listMessages({ limit, agentId });
158
+ renderMessages(payload);
159
+ }
160
+
161
+ async function sendMessage() {
162
+ const body = parseFlag("body", "");
163
+ if (!body.trim()) {
164
+ throw new Error("Missing --body=... for send");
165
+ }
166
+ const result = await client.sendMessage(body);
167
+ console.log(toPrettyJson(result));
168
+ }
169
+
170
+ async function watchMessages() {
171
+ const limit = Number(parseFlag("limit", "12")) || 12;
172
+ const intervalSec = Math.max(1, Number(parseFlag("interval", "5")) || 5);
173
+ const agentId = parseFlag("agent-id", "");
174
+ for await (const item of client.watchMessages({ limit, intervalSec, agentId })) {
175
+ const stamp = item.created_at ? new Date(item.created_at).toLocaleString() : "-";
176
+ console.log(`${paint("[message]", COLOR.bold, COLOR.orange)} ${item.display_name || "(unnamed)"} ${paint(stamp, COLOR.dim)}`);
177
+ console.log(item.body || "");
178
+ console.log("");
179
+ }
180
+ }
181
+
182
+ async function main() {
183
+ if (cmd === "help" || cmd === "--help" || cmd === "-h") {
184
+ printHelp();
185
+ return;
186
+ }
187
+ if (cmd === "status") {
188
+ await showStatus();
189
+ return;
190
+ }
191
+ if (cmd === "profile") {
192
+ await showProfile();
193
+ return;
194
+ }
195
+ if (cmd === "config") {
196
+ await showConfig();
197
+ return;
198
+ }
199
+ if (cmd === "messages") {
200
+ await listMessages();
201
+ return;
202
+ }
203
+ if (cmd === "send") {
204
+ await sendMessage();
205
+ return;
206
+ }
207
+ if (cmd === "watch") {
208
+ await watchMessages();
209
+ return;
210
+ }
211
+
212
+ if (hasFlag("json")) {
213
+ console.log(toPrettyJson({ command: cmd, api_base: API_BASE }));
214
+ return;
215
+ }
216
+
217
+ throw new Error(`Unknown command: ${cmd}`);
218
+ }
219
+
220
+ main().catch((error) => {
221
+ console.error(paint(`Bridge client failed: ${error instanceof Error ? error.message : String(error)}`, COLOR.bold, COLOR.red));
222
+ process.exit(1);
223
+ });