@silicaclaw/cli 2026.3.19-1 → 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.
- package/CHANGELOG.md +1 -1
- package/INSTALL.md +31 -0
- package/README.md +28 -0
- package/VERSION +1 -1
- package/apps/local-console/public/index.html +1327 -245
- package/apps/local-console/src/server.ts +439 -10
- package/docs/OPENCLAW_BRIDGE.md +85 -0
- package/docs/OPENCLAW_BRIDGE_ZH.md +90 -0
- package/openclaw-skills/silicaclaw-broadcast/SKILL.md +89 -0
- package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -0
- package/openclaw-skills/silicaclaw-broadcast/agents/openai.yaml +6 -0
- package/openclaw-skills/silicaclaw-broadcast/manifest.json +34 -0
- package/openclaw-skills/silicaclaw-broadcast/references/computer-control-via-openclaw.md +41 -0
- package/openclaw-skills/silicaclaw-broadcast/references/owner-dispatch-adapter.md +81 -0
- package/openclaw-skills/silicaclaw-broadcast/references/owner-forwarding-policy.md +48 -0
- package/openclaw-skills/silicaclaw-broadcast/scripts/bridge-client.mjs +59 -0
- package/openclaw-skills/silicaclaw-broadcast/scripts/owner-dispatch-adapter-demo.mjs +12 -0
- package/openclaw-skills/silicaclaw-broadcast/scripts/owner-forwarder-demo.mjs +111 -0
- package/openclaw-skills/silicaclaw-broadcast/scripts/send-to-owner-via-openclaw.mjs +69 -0
- package/package.json +2 -1
- package/packages/core/dist/socialConfig.js +1 -1
- package/packages/core/dist/socialTemplate.js +1 -1
- package/packages/core/src/socialConfig.ts +1 -1
- package/packages/core/src/socialTemplate.ts +1 -1
- package/packages/network/dist/relayPreview.js +16 -4
- package/packages/network/src/relayPreview.ts +17 -4
- package/scripts/functional-check.mjs +29 -0
- package/scripts/install-openclaw-skill.mjs +54 -0
- package/scripts/openclaw-bridge-adapter.mjs +7 -0
- package/scripts/openclaw-bridge-client.mjs +42 -0
- package/scripts/pack-openclaw-skill.mjs +58 -0
- package/scripts/silicaclaw-cli.mjs +18 -0
- package/scripts/validate-openclaw-skill.mjs +74 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
const API_BASE = String(process.env.SILICACLAW_API_BASE || "http://localhost:4310").replace(/\/+$/, "");
|
|
6
|
+
const POLL_INTERVAL_MS = Math.max(1000, Number(process.env.OPENCLAW_FORWARDER_INTERVAL_MS || 5000) || 5000);
|
|
7
|
+
const LIMIT = Math.max(1, Number(process.env.OPENCLAW_FORWARDER_LIMIT || 20) || 20);
|
|
8
|
+
const OWNER_FORWARD_CMD = String(process.env.OPENCLAW_OWNER_FORWARD_CMD || "").trim();
|
|
9
|
+
|
|
10
|
+
function scoreMessage(message) {
|
|
11
|
+
const text = String(message?.body || "").toLowerCase();
|
|
12
|
+
if (!text) return "learn_only";
|
|
13
|
+
if (
|
|
14
|
+
text.includes("error") ||
|
|
15
|
+
text.includes("failed") ||
|
|
16
|
+
text.includes("failure") ||
|
|
17
|
+
text.includes("blocked") ||
|
|
18
|
+
text.includes("approval") ||
|
|
19
|
+
text.includes("security") ||
|
|
20
|
+
text.includes("credential") ||
|
|
21
|
+
text.includes("payment") ||
|
|
22
|
+
text.includes("fund") ||
|
|
23
|
+
text.includes("deploy") ||
|
|
24
|
+
text.includes("completed")
|
|
25
|
+
) {
|
|
26
|
+
return "forward_summary";
|
|
27
|
+
}
|
|
28
|
+
return "learn_only";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function summarizeForOwner(message) {
|
|
32
|
+
const source = `${message.display_name || "Unknown"} (${message.topic || "global"})`;
|
|
33
|
+
const body = String(message.body || "").trim();
|
|
34
|
+
return [
|
|
35
|
+
`Source: ${source}`,
|
|
36
|
+
"Why it matters: a SilicaClaw public broadcast matched the OpenClaw owner-forwarding policy.",
|
|
37
|
+
`What happened: ${body.slice(0, 220)}${body.length > 220 ? "..." : ""}`,
|
|
38
|
+
`Action: Review if owner follow-up is needed.`,
|
|
39
|
+
].join("\n");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function dispatchToOwner(route, summary, message) {
|
|
43
|
+
if (!OWNER_FORWARD_CMD) {
|
|
44
|
+
console.log("");
|
|
45
|
+
console.log(`[${route}]`);
|
|
46
|
+
console.log(summary);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await new Promise((resolve, reject) => {
|
|
51
|
+
const child = spawn(OWNER_FORWARD_CMD, {
|
|
52
|
+
shell: true,
|
|
53
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
54
|
+
env: process.env,
|
|
55
|
+
});
|
|
56
|
+
child.on("error", reject);
|
|
57
|
+
child.on("exit", (code) => {
|
|
58
|
+
if (code === 0) {
|
|
59
|
+
resolve();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
reject(new Error(`owner dispatch failed (exit=${code ?? "unknown"})`));
|
|
63
|
+
});
|
|
64
|
+
child.stdin.write(JSON.stringify({
|
|
65
|
+
route,
|
|
66
|
+
summary,
|
|
67
|
+
message: {
|
|
68
|
+
message_id: message.message_id || "",
|
|
69
|
+
display_name: message.display_name || "",
|
|
70
|
+
topic: message.topic || "global",
|
|
71
|
+
body: message.body || "",
|
|
72
|
+
},
|
|
73
|
+
}, null, 2));
|
|
74
|
+
child.stdin.end();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function request(path, options = {}) {
|
|
79
|
+
const res = await fetch(`${API_BASE}${path}`, {
|
|
80
|
+
headers: { "Content-Type": "application/json" },
|
|
81
|
+
...options,
|
|
82
|
+
});
|
|
83
|
+
const json = await res.json().catch(() => null);
|
|
84
|
+
if (!res.ok || !json?.ok) {
|
|
85
|
+
throw new Error(json?.error?.message || `Request failed (${res.status})`);
|
|
86
|
+
}
|
|
87
|
+
return json.data;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function main() {
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
console.log(`OpenClaw owner forwarder demo watching ${API_BASE}`);
|
|
93
|
+
while (true) {
|
|
94
|
+
const payload = await request(`/api/openclaw/bridge/messages?limit=${LIMIT}`);
|
|
95
|
+
const items = Array.isArray(payload?.items) ? payload.items.slice().reverse() : [];
|
|
96
|
+
for (const item of items) {
|
|
97
|
+
if (!item?.message_id || seen.has(item.message_id)) continue;
|
|
98
|
+
seen.add(item.message_id);
|
|
99
|
+
const route = scoreMessage(item);
|
|
100
|
+
if (route === "learn_only") continue;
|
|
101
|
+
const summary = summarizeForOwner(item);
|
|
102
|
+
await dispatchToOwner(route, summary, item);
|
|
103
|
+
}
|
|
104
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
main().catch((error) => {
|
|
109
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
110
|
+
process.exit(1);
|
|
111
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
|
|
7
|
+
function requiredEnv(name) {
|
|
8
|
+
const value = String(process.env[name] || "").trim();
|
|
9
|
+
if (!value) {
|
|
10
|
+
throw new Error(`Missing required environment variable: ${name}`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function resolveOpenClawCommand() {
|
|
16
|
+
const explicitBin = String(process.env.OPENCLAW_BIN || "").trim();
|
|
17
|
+
if (explicitBin) {
|
|
18
|
+
return { cmd: explicitBin, args: [] };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const sourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
|
|
22
|
+
if (sourceDir) {
|
|
23
|
+
return {
|
|
24
|
+
cmd: "node",
|
|
25
|
+
args: [resolve(sourceDir, "openclaw.mjs")],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { cmd: "openclaw", args: [] };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function main() {
|
|
33
|
+
const payload = JSON.parse(readFileSync(0, "utf8"));
|
|
34
|
+
const channel = requiredEnv("OPENCLAW_OWNER_CHANNEL");
|
|
35
|
+
const target = requiredEnv("OPENCLAW_OWNER_TARGET");
|
|
36
|
+
const account = String(process.env.OPENCLAW_OWNER_ACCOUNT || "").trim();
|
|
37
|
+
const message = String(payload?.summary || "").trim();
|
|
38
|
+
if (!message) {
|
|
39
|
+
throw new Error("Missing summary in stdin payload");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const command = resolveOpenClawCommand();
|
|
43
|
+
const args = [
|
|
44
|
+
...command.args,
|
|
45
|
+
"message",
|
|
46
|
+
"send",
|
|
47
|
+
"--channel",
|
|
48
|
+
channel,
|
|
49
|
+
"--target",
|
|
50
|
+
target,
|
|
51
|
+
"--message",
|
|
52
|
+
message,
|
|
53
|
+
];
|
|
54
|
+
if (account) {
|
|
55
|
+
args.push("--account", account);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const result = spawnSync(command.cmd, args, {
|
|
59
|
+
stdio: "inherit",
|
|
60
|
+
env: process.env,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (result.error) {
|
|
64
|
+
throw result.error;
|
|
65
|
+
}
|
|
66
|
+
process.exit(result.status ?? 1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silicaclaw/cli",
|
|
3
|
-
"version": "2026.3.19-
|
|
3
|
+
"version": "2026.3.19-2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"packages/storage/src/**",
|
|
28
28
|
"packages/storage/tsconfig.json",
|
|
29
29
|
"scripts/",
|
|
30
|
+
"openclaw-skills/",
|
|
30
31
|
"docs/",
|
|
31
32
|
"social.md.example",
|
|
32
33
|
"openclaw.social.md.example",
|
|
@@ -263,7 +263,7 @@ function generateDefaultSocialMdTemplate(options = {}) {
|
|
|
263
263
|
const displayName = options.display_name?.trim() || "My OpenClaw Agent";
|
|
264
264
|
const bio = options.bio?.trim() || "Local AI agent running on this machine";
|
|
265
265
|
const tags = Array.isArray(options.tags) && options.tags.length > 0 ? options.tags : ["openclaw", "local-first"];
|
|
266
|
-
const mode = options.mode ?? "
|
|
266
|
+
const mode = options.mode ?? "global-preview";
|
|
267
267
|
const publicEnabled = typeof options.public_enabled === "boolean" ? options.public_enabled : false;
|
|
268
268
|
return `---
|
|
269
269
|
enabled: true
|
|
@@ -34,7 +34,7 @@ function generateSocialMdTemplate(runtimeConfig) {
|
|
|
34
34
|
const tags = asStringArray(profile?.tags, ["openclaw", "local-first"]);
|
|
35
35
|
const mode = network?.mode === "local" || network?.mode === "lan" || network?.mode === "global-preview"
|
|
36
36
|
? network.mode
|
|
37
|
-
: "
|
|
37
|
+
: "global-preview";
|
|
38
38
|
const discoverable = asBool(discovery?.discoverable, true);
|
|
39
39
|
const allowProfileBroadcast = asBool(discovery?.allow_profile_broadcast, true);
|
|
40
40
|
const allowPresenceBroadcast = asBool(discovery?.allow_presence_broadcast, true);
|
|
@@ -420,7 +420,7 @@ export function generateDefaultSocialMdTemplate(options: DefaultSocialTemplateOp
|
|
|
420
420
|
const displayName = options.display_name?.trim() || "My OpenClaw Agent";
|
|
421
421
|
const bio = options.bio?.trim() || "Local AI agent running on this machine";
|
|
422
422
|
const tags = Array.isArray(options.tags) && options.tags.length > 0 ? options.tags : ["openclaw", "local-first"];
|
|
423
|
-
const mode = options.mode ?? "
|
|
423
|
+
const mode = options.mode ?? "global-preview";
|
|
424
424
|
const publicEnabled = typeof options.public_enabled === "boolean" ? options.public_enabled : false;
|
|
425
425
|
return `---
|
|
426
426
|
enabled: true
|
|
@@ -39,7 +39,7 @@ export function generateSocialMdTemplate(runtimeConfig: SocialRuntimeConfig | nu
|
|
|
39
39
|
const mode =
|
|
40
40
|
network?.mode === "local" || network?.mode === "lan" || network?.mode === "global-preview"
|
|
41
41
|
? network.mode
|
|
42
|
-
: "
|
|
42
|
+
: "global-preview";
|
|
43
43
|
|
|
44
44
|
const discoverable = asBool(discovery?.discoverable, true);
|
|
45
45
|
const allowProfileBroadcast = asBool(discovery?.allow_profile_broadcast, true);
|
|
@@ -357,18 +357,20 @@ class RelayPreviewAdapter {
|
|
|
357
357
|
const endpoint = this.signalingEndpoints[index]?.replace(/\/+$/, "");
|
|
358
358
|
if (!endpoint)
|
|
359
359
|
continue;
|
|
360
|
+
let timeout = null;
|
|
360
361
|
try {
|
|
361
362
|
const controller = new AbortController();
|
|
362
|
-
|
|
363
|
+
timeout = setTimeout(() => controller.abort(), this.requestTimeoutMs);
|
|
363
364
|
const response = await fetch(`${endpoint}${path}`, {
|
|
364
365
|
method,
|
|
365
366
|
headers: method === "POST" ? { "content-type": "application/json" } : undefined,
|
|
366
367
|
body: method === "POST" ? JSON.stringify(body) : undefined,
|
|
367
368
|
signal: controller.signal,
|
|
368
369
|
});
|
|
369
|
-
clearTimeout(timeout);
|
|
370
370
|
if (!response.ok) {
|
|
371
|
-
|
|
371
|
+
const responseText = (await response.text().catch(() => "")).trim();
|
|
372
|
+
const detail = responseText ? `: ${responseText.slice(0, 200)}` : "";
|
|
373
|
+
throw new Error(`${method} ${path} failed (${response.status})${detail}`);
|
|
372
374
|
}
|
|
373
375
|
this.activeEndpointIndex = index;
|
|
374
376
|
this.activeEndpoint = endpoint;
|
|
@@ -376,7 +378,12 @@ class RelayPreviewAdapter {
|
|
|
376
378
|
return response.json();
|
|
377
379
|
}
|
|
378
380
|
catch (error) {
|
|
379
|
-
const
|
|
381
|
+
const isAbort = error instanceof Error && (error.name === "AbortError" || error.message === "This operation was aborted");
|
|
382
|
+
const message = isAbort
|
|
383
|
+
? `${method} ${path} timed out after ${this.requestTimeoutMs}ms`
|
|
384
|
+
: error instanceof Error
|
|
385
|
+
? error.message
|
|
386
|
+
: String(error);
|
|
380
387
|
errors.push(`${endpoint}: ${message}`);
|
|
381
388
|
this.stats.signaling_errors += 1;
|
|
382
389
|
this.lastError = message;
|
|
@@ -384,6 +391,11 @@ class RelayPreviewAdapter {
|
|
|
384
391
|
this.reconnectAttemptsTotal += 1;
|
|
385
392
|
this.recordDiscovery("signaling_error", { endpoint, detail: message });
|
|
386
393
|
}
|
|
394
|
+
finally {
|
|
395
|
+
if (timeout) {
|
|
396
|
+
clearTimeout(timeout);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
387
399
|
}
|
|
388
400
|
throw new Error(errors.join(" | "));
|
|
389
401
|
}
|
|
@@ -480,31 +480,44 @@ export class RelayPreviewAdapter implements NetworkAdapter {
|
|
|
480
480
|
const index = (this.activeEndpointIndex + offset) % this.signalingEndpoints.length;
|
|
481
481
|
const endpoint = this.signalingEndpoints[index]?.replace(/\/+$/, "");
|
|
482
482
|
if (!endpoint) continue;
|
|
483
|
+
let timeout: NodeJS.Timeout | null = null;
|
|
483
484
|
try {
|
|
484
485
|
const controller = new AbortController();
|
|
485
|
-
|
|
486
|
+
timeout = setTimeout(() => controller.abort(), this.requestTimeoutMs);
|
|
486
487
|
const response = await fetch(`${endpoint}${path}`, {
|
|
487
488
|
method,
|
|
488
489
|
headers: method === "POST" ? { "content-type": "application/json" } : undefined,
|
|
489
490
|
body: method === "POST" ? JSON.stringify(body) : undefined,
|
|
490
491
|
signal: controller.signal,
|
|
491
492
|
});
|
|
492
|
-
clearTimeout(timeout);
|
|
493
493
|
if (!response.ok) {
|
|
494
|
-
|
|
494
|
+
const responseText = (await response.text().catch(() => "")).trim();
|
|
495
|
+
const detail = responseText ? `: ${responseText.slice(0, 200)}` : "";
|
|
496
|
+
throw new Error(`${method} ${path} failed (${response.status})${detail}`);
|
|
495
497
|
}
|
|
496
498
|
this.activeEndpointIndex = index;
|
|
497
499
|
this.activeEndpoint = endpoint;
|
|
498
500
|
this.lastError = null;
|
|
499
501
|
return response.json();
|
|
500
502
|
} catch (error) {
|
|
501
|
-
const
|
|
503
|
+
const isAbort = error instanceof Error && (
|
|
504
|
+
error.name === "AbortError" || error.message === "This operation was aborted"
|
|
505
|
+
);
|
|
506
|
+
const message = isAbort
|
|
507
|
+
? `${method} ${path} timed out after ${this.requestTimeoutMs}ms`
|
|
508
|
+
: error instanceof Error
|
|
509
|
+
? error.message
|
|
510
|
+
: String(error);
|
|
502
511
|
errors.push(`${endpoint}: ${message}`);
|
|
503
512
|
this.stats.signaling_errors += 1;
|
|
504
513
|
this.lastError = message;
|
|
505
514
|
this.lastErrorAt = Date.now();
|
|
506
515
|
this.reconnectAttemptsTotal += 1;
|
|
507
516
|
this.recordDiscovery("signaling_error", { endpoint, detail: message });
|
|
517
|
+
} finally {
|
|
518
|
+
if (timeout) {
|
|
519
|
+
clearTimeout(timeout);
|
|
520
|
+
}
|
|
508
521
|
}
|
|
509
522
|
}
|
|
510
523
|
throw new Error(errors.join(" | "));
|
|
@@ -55,6 +55,17 @@ async function main() {
|
|
|
55
55
|
'ROADMAP.md',
|
|
56
56
|
'CHANGELOG.md',
|
|
57
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',
|
|
58
69
|
'apps/local-console/public/index.html',
|
|
59
70
|
'apps/public-explorer/public/index.html',
|
|
60
71
|
'data/cache.json',
|
|
@@ -78,6 +89,19 @@ async function main() {
|
|
|
78
89
|
checkInlineScriptSyntax(path.resolve(root, 'apps/local-console/public/index.html'));
|
|
79
90
|
checkInlineScriptSyntax(path.resolve(root, 'apps/public-explorer/public/index.html'));
|
|
80
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
|
+
|
|
81
105
|
// Import built modules (requires npm run build)
|
|
82
106
|
const core = await import(pathToFileURL(path.resolve(root, 'packages/core/dist/index.js')).href);
|
|
83
107
|
const network = await import(pathToFileURL(path.resolve(root, 'packages/network/dist/index.js')).href);
|
|
@@ -185,6 +209,11 @@ async function main() {
|
|
|
185
209
|
try {
|
|
186
210
|
const bridgeStatus = service.getOpenClawBridgeStatus();
|
|
187
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');
|
|
188
217
|
|
|
189
218
|
const bridgeProfile = service.getOpenClawBridgeProfile();
|
|
190
219
|
assert(bridgeProfile?.profile, 'Bridge profile payload missing profile');
|
|
@@ -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();
|
|
@@ -26,6 +26,9 @@ export function createOpenClawBridgeClient(options = {}) {
|
|
|
26
26
|
async getStatus() {
|
|
27
27
|
return bridgeRequest(apiBase, "/api/openclaw/bridge");
|
|
28
28
|
},
|
|
29
|
+
async getConfig() {
|
|
30
|
+
return bridgeRequest(apiBase, "/api/openclaw/bridge/config");
|
|
31
|
+
},
|
|
29
32
|
async getProfile() {
|
|
30
33
|
return bridgeRequest(apiBase, "/api/openclaw/bridge/profile");
|
|
31
34
|
},
|
|
@@ -73,6 +76,10 @@ export async function getOpenClawBridgeProfile(options = {}) {
|
|
|
73
76
|
return createOpenClawBridgeClient(options).getProfile();
|
|
74
77
|
}
|
|
75
78
|
|
|
79
|
+
export async function getOpenClawBridgeConfig(options = {}) {
|
|
80
|
+
return createOpenClawBridgeClient(options).getConfig();
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
export async function listOpenClawBridgeMessages(options = {}) {
|
|
77
84
|
return createOpenClawBridgeClient(options).listMessages(options);
|
|
78
85
|
}
|
|
@@ -57,6 +57,7 @@ function printHelp() {
|
|
|
57
57
|
section("Commands");
|
|
58
58
|
kv("help", "Show this help");
|
|
59
59
|
kv("status", "Show bridge status");
|
|
60
|
+
kv("config", "Show suggested OpenClaw config");
|
|
60
61
|
kv("profile", "Show resolved identity/profile payload");
|
|
61
62
|
kv("messages", "List recent public messages");
|
|
62
63
|
kv("send --body=...", "Publish a signed public message");
|
|
@@ -102,6 +103,38 @@ async function showStatus() {
|
|
|
102
103
|
kv("identity_source", status.identity_source || "-");
|
|
103
104
|
kv("social_source", status.social_source_path || "-");
|
|
104
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("");
|
|
105
138
|
section("Endpoints");
|
|
106
139
|
for (const [key, value] of Object.entries(status.endpoints || {})) {
|
|
107
140
|
kv(key, `${API_BASE}${value}`);
|
|
@@ -113,6 +146,11 @@ async function showProfile() {
|
|
|
113
146
|
console.log(toPrettyJson(profile));
|
|
114
147
|
}
|
|
115
148
|
|
|
149
|
+
async function showConfig() {
|
|
150
|
+
const config = await client.getConfig();
|
|
151
|
+
console.log(toPrettyJson(config));
|
|
152
|
+
}
|
|
153
|
+
|
|
116
154
|
async function listMessages() {
|
|
117
155
|
const limit = Number(parseFlag("limit", "24")) || 24;
|
|
118
156
|
const agentId = parseFlag("agent-id", "");
|
|
@@ -154,6 +192,10 @@ async function main() {
|
|
|
154
192
|
await showProfile();
|
|
155
193
|
return;
|
|
156
194
|
}
|
|
195
|
+
if (cmd === "config") {
|
|
196
|
+
await showConfig();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
157
199
|
if (cmd === "messages") {
|
|
158
200
|
await listMessages();
|
|
159
201
|
return;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
const ROOT_DIR = resolve(__dirname, "..");
|
|
12
|
+
|
|
13
|
+
function parseFlag(name, fallback = "") {
|
|
14
|
+
const prefix = `--${name}=`;
|
|
15
|
+
for (const item of process.argv.slice(2)) {
|
|
16
|
+
if (item.startsWith(prefix)) return item.slice(prefix.length);
|
|
17
|
+
}
|
|
18
|
+
return fallback;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function main() {
|
|
22
|
+
const skillName = parseFlag("skill", "silicaclaw-broadcast");
|
|
23
|
+
const skillsRoot = resolve(ROOT_DIR, "openclaw-skills");
|
|
24
|
+
const skillDir = resolve(skillsRoot, skillName);
|
|
25
|
+
if (!existsSync(skillDir)) {
|
|
26
|
+
throw new Error(`Skill not found: ${skillName}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const outDir = resolve(ROOT_DIR, "dist", "openclaw-skills");
|
|
30
|
+
mkdirSync(outDir, { recursive: true });
|
|
31
|
+
const archivePath = resolve(outDir, `${skillName}.tgz`);
|
|
32
|
+
|
|
33
|
+
const result = spawnSync("tar", ["-czf", archivePath, "-C", skillsRoot, skillName], {
|
|
34
|
+
cwd: ROOT_DIR,
|
|
35
|
+
stdio: "pipe",
|
|
36
|
+
encoding: "utf8",
|
|
37
|
+
});
|
|
38
|
+
if (result.error) {
|
|
39
|
+
throw result.error;
|
|
40
|
+
}
|
|
41
|
+
if ((result.status ?? 1) !== 0) {
|
|
42
|
+
throw new Error(String(result.stderr || result.stdout || "tar failed").trim());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const digest = createHash("sha256").update(readFileSync(archivePath)).digest("hex");
|
|
46
|
+
const checksumPath = `${archivePath}.sha256`;
|
|
47
|
+
writeFileSync(checksumPath, `${digest} ${resolve(outDir, `${skillName}.tgz`).split("/").pop()}\n`, "utf8");
|
|
48
|
+
|
|
49
|
+
console.log(JSON.stringify({
|
|
50
|
+
skill: skillName,
|
|
51
|
+
source_dir: skillDir,
|
|
52
|
+
archive_path: archivePath,
|
|
53
|
+
sha256: digest,
|
|
54
|
+
checksum_path: checksumPath,
|
|
55
|
+
}, null, 2));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
main();
|
|
@@ -486,6 +486,9 @@ function help() {
|
|
|
486
486
|
kv("Connect", "silicaclaw connect");
|
|
487
487
|
kv("OpenClaw Demo", "silicaclaw openclaw-demo");
|
|
488
488
|
kv("OpenClaw Bridge", "silicaclaw openclaw-bridge status");
|
|
489
|
+
kv("OpenClaw Skill", "silicaclaw openclaw-skill-install");
|
|
490
|
+
kv("Pack Skill", "silicaclaw openclaw-skill-pack");
|
|
491
|
+
kv("Check Skill", "silicaclaw openclaw-skill-validate");
|
|
489
492
|
kv("Logs", "silicaclaw logs local-console");
|
|
490
493
|
kv("Doctor", "silicaclaw doctor");
|
|
491
494
|
kv("Help", "silicaclaw help");
|
|
@@ -527,6 +530,21 @@ switch (cmd) {
|
|
|
527
530
|
cwd: process.cwd(),
|
|
528
531
|
});
|
|
529
532
|
break;
|
|
533
|
+
case "openclaw-skill-install":
|
|
534
|
+
run("node", [resolve(ROOT_DIR, "scripts", "install-openclaw-skill.mjs"), ...process.argv.slice(3)], {
|
|
535
|
+
cwd: process.cwd(),
|
|
536
|
+
});
|
|
537
|
+
break;
|
|
538
|
+
case "openclaw-skill-pack":
|
|
539
|
+
run("node", [resolve(ROOT_DIR, "scripts", "pack-openclaw-skill.mjs"), ...process.argv.slice(3)], {
|
|
540
|
+
cwd: process.cwd(),
|
|
541
|
+
});
|
|
542
|
+
break;
|
|
543
|
+
case "openclaw-skill-validate":
|
|
544
|
+
run("node", [resolve(ROOT_DIR, "scripts", "validate-openclaw-skill.mjs"), ...process.argv.slice(3)], {
|
|
545
|
+
cwd: process.cwd(),
|
|
546
|
+
});
|
|
547
|
+
break;
|
|
530
548
|
case "start":
|
|
531
549
|
case "stop":
|
|
532
550
|
case "restart":
|