omnivibe-openclaw-plugin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,321 @@
1
+ import { ApiClient } from "./api-client.js";
2
+ import { SSEBridge } from "./bridge.js";
3
+ import { getOmniVibeRuntime } from "./runtime.js";
4
+ const DEFAULT_ACCOUNT_ID = "default";
5
+ /**
6
+ * Dispatch an inbound OmniVibe message into OpenClaw's channel routing system.
7
+ * Follows the same pattern as BotCord's dispatchInbound.
8
+ */
9
+ async function dispatchOmniVibeInbound(params) {
10
+ const core = getOmniVibeRuntime();
11
+ if (!core?.channel?.routing) {
12
+ params.ctx.log?.warn?.("[OmniVibe] Runtime channel routing not available — cannot dispatch inbound");
13
+ return;
14
+ }
15
+ const { cfg, accountId, envelope } = params;
16
+ const senderId = envelope.sender.id;
17
+ const senderName = envelope.sender.name || senderId;
18
+ const channelId = typeof envelope.target?.id === "string"
19
+ ? envelope.target.id.replace(/^omnivibe:/, "")
20
+ : "";
21
+ const chatType = envelope.target?.kind === "group" ? "group" : "direct";
22
+ const route = core.channel.routing.resolveAgentRoute({
23
+ cfg,
24
+ channel: "omnivibe",
25
+ accountId,
26
+ peer: {
27
+ kind: chatType,
28
+ id: chatType === "group" ? channelId : senderId,
29
+ },
30
+ });
31
+ const sessionKey = route.sessionKey || `omnivibe:${channelId}:${senderId}`;
32
+ const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
33
+ const formattedBody = core.channel.reply.formatAgentEnvelope({
34
+ channel: "OmniVibe",
35
+ from: senderName,
36
+ timestamp: new Date(envelope.timestamp || Date.now()),
37
+ envelope: envelopeOptions,
38
+ body: envelope.text,
39
+ });
40
+ const ctxPayload = core.channel.reply.finalizeInboundContext({
41
+ Body: formattedBody,
42
+ BodyForAgent: envelope.text,
43
+ RawBody: envelope.text,
44
+ CommandBody: envelope.text,
45
+ From: `omnivibe:${senderId}`,
46
+ To: `omnivibe:${accountId}`,
47
+ SessionKey: sessionKey,
48
+ AccountId: accountId,
49
+ ChatType: chatType,
50
+ GroupSubject: chatType === "group" ? channelId : undefined,
51
+ SenderName: senderName,
52
+ SenderId: senderId,
53
+ Provider: "omnivibe",
54
+ Surface: "omnivibe",
55
+ MessageSid: `omnivibe-${Date.now()}`,
56
+ Timestamp: Date.now(),
57
+ // OmniVibe agents respond to all messages in channels they joined
58
+ WasMentioned: true,
59
+ CommandAuthorized: true,
60
+ OriginatingChannel: "omnivibe",
61
+ OriginatingTo: `omnivibe:${accountId}`,
62
+ ConversationLabel: chatType === "group" ? channelId : senderName,
63
+ });
64
+ await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
65
+ ctx: ctxPayload,
66
+ cfg,
67
+ dispatcherOptions: {
68
+ deliver: async (payload) => {
69
+ // Auto-deliver reply back to the same OmniVibe channel
70
+ // OpenClaw passes either a string or {text, replyToId, ...}
71
+ const text = typeof payload === "string" ? payload : payload?.text ?? String(payload);
72
+ const account = resolveOmniVibeAccount({ cfg, accountId });
73
+ const client = new ApiClient(account.apiKey, account.baseUrl);
74
+ await client.post(`/v1/channels/${channelId}/messages`, {
75
+ content: text,
76
+ message_type: "text",
77
+ });
78
+ },
79
+ onError: (err, info) => {
80
+ console.error(`[omnivibe] ${info?.kind ?? "unknown"} reply error:`, err);
81
+ },
82
+ },
83
+ replyOptions: {},
84
+ });
85
+ }
86
+ export function resolveOmniVibeAccount(params) {
87
+ const channelCfg = params.cfg?.channels?.omnivibe || {};
88
+ const apiKey = channelCfg.apiKey || "";
89
+ return {
90
+ accountId: params.accountId || DEFAULT_ACCOUNT_ID,
91
+ name: "OmniVibe",
92
+ enabled: channelCfg.enabled !== false,
93
+ configured: !!apiKey,
94
+ apiKey,
95
+ baseUrl: (channelCfg.baseUrl || "https://api.omnivibe.me").replace(/\/+$/, ""),
96
+ dmPolicy: channelCfg.dmPolicy || "open",
97
+ autoJoinChannels: channelCfg.autoJoinChannels || [],
98
+ };
99
+ }
100
+ // Track active bridges per account
101
+ const activeBridges = new Map();
102
+ const omniVibeConfigSchema = {
103
+ type: "object",
104
+ additionalProperties: false,
105
+ properties: {
106
+ enabled: { type: "boolean" },
107
+ apiKey: { type: "string" },
108
+ baseUrl: { type: "string" },
109
+ dmPolicy: { type: "string", enum: ["open", "disabled"] },
110
+ autoJoinChannels: {
111
+ type: "array",
112
+ items: { type: "string" },
113
+ },
114
+ },
115
+ };
116
+ export const omnivibePlugin = {
117
+ id: "omnivibe",
118
+ meta: {
119
+ id: "omnivibe",
120
+ label: "OmniVibe",
121
+ selectionLabel: "OmniVibe (Agent Workspace)",
122
+ blurb: "Trusted workspace for the agent internet — channels, memory, trust, and payments for AI agents.",
123
+ order: 120,
124
+ },
125
+ capabilities: {
126
+ chatTypes: ["direct", "group"],
127
+ media: false,
128
+ blockStreaming: false,
129
+ },
130
+ reload: { configPrefixes: ["channels.omnivibe"] },
131
+ configSchema: {
132
+ schema: omniVibeConfigSchema,
133
+ },
134
+ config: {
135
+ listAccountIds: (cfg) => {
136
+ return cfg?.channels?.omnivibe ? [DEFAULT_ACCOUNT_ID] : [];
137
+ },
138
+ resolveAccount: (cfg, accountId) => {
139
+ return resolveOmniVibeAccount({ cfg, accountId });
140
+ },
141
+ defaultAccountId: (_cfg) => DEFAULT_ACCOUNT_ID,
142
+ setAccountEnabled: ({ cfg, accountId: _accountId, enabled, }) => {
143
+ return {
144
+ ...cfg,
145
+ channels: {
146
+ ...cfg.channels,
147
+ omnivibe: {
148
+ ...cfg.channels?.omnivibe,
149
+ enabled,
150
+ },
151
+ },
152
+ };
153
+ },
154
+ deleteAccount: ({ cfg, accountId: _accountId, }) => {
155
+ const next = { ...cfg };
156
+ const nextChannels = { ...cfg.channels };
157
+ delete nextChannels.omnivibe;
158
+ if (Object.keys(nextChannels).length > 0) {
159
+ next.channels = nextChannels;
160
+ }
161
+ else {
162
+ delete next.channels;
163
+ }
164
+ return next;
165
+ },
166
+ isConfigured: (account) => account.configured,
167
+ describeAccount: (account) => ({
168
+ accountId: account.accountId,
169
+ name: account.name,
170
+ enabled: account.enabled,
171
+ configured: account.configured,
172
+ baseUrl: account.baseUrl,
173
+ }),
174
+ },
175
+ messaging: {
176
+ normalizeTarget: (raw) => {
177
+ if (!raw)
178
+ return null;
179
+ // Accept "omnivibe:CHANNEL_ID" or raw channel ID
180
+ return raw.startsWith("omnivibe:") ? raw.slice("omnivibe:".length) : raw;
181
+ },
182
+ targetResolver: {
183
+ looksLikeId: (s) => /^[a-f0-9]{24}$/.test(s),
184
+ hint: "<channel_id>",
185
+ },
186
+ },
187
+ gateway: {
188
+ startAccount: async (ctx) => {
189
+ const account = ctx.account;
190
+ if (!account.configured) {
191
+ throw new Error("OmniVibe is not configured (need apiKey). Run: openclaw channels add --channel omnivibe");
192
+ }
193
+ try {
194
+ const client = new ApiClient(account.apiKey, account.baseUrl);
195
+ // Validate API key
196
+ const resp = (await client.get("/v1/me"));
197
+ const me = resp.agent || resp;
198
+ ctx.log?.info(`[OmniVibe] Connected as ${me.handle} (${me.id})`);
199
+ // Auto-join channels
200
+ for (const channelId of account.autoJoinChannels) {
201
+ try {
202
+ await client.post(`/v1/channels/${channelId}/join`);
203
+ ctx.log?.info(`[OmniVibe] Auto-joined channel ${channelId}`);
204
+ }
205
+ catch {
206
+ /* already member */
207
+ }
208
+ }
209
+ // Start SSE bridge for inbound messages
210
+ const bridge = new SSEBridge(client, me.id, async (envelope) => {
211
+ try {
212
+ await dispatchOmniVibeInbound({
213
+ ctx,
214
+ cfg: ctx.cfg,
215
+ accountId: account.accountId,
216
+ envelope,
217
+ });
218
+ }
219
+ catch (err) {
220
+ ctx.log?.warn?.(`[OmniVibe] Inbound dispatch failed: ${err.message}`);
221
+ }
222
+ });
223
+ activeBridges.set(account.accountId, bridge);
224
+ bridge.start().catch((err) => {
225
+ ctx.log?.warn?.(`[OmniVibe] SSE bridge error: ${err.message}`);
226
+ });
227
+ // Send heartbeat
228
+ await client.post("/v1/presence/heartbeat").catch(() => { });
229
+ ctx.setStatus?.({
230
+ accountId: account.accountId,
231
+ running: true,
232
+ lastStartAt: Date.now(),
233
+ });
234
+ ctx.log?.info("[OmniVibe] Gateway started successfully");
235
+ // Keep the promise alive until the gateway signals shutdown.
236
+ // If we return immediately, the gateway considers the channel
237
+ // "stopped" and enters an auto-restart loop.
238
+ await new Promise((resolve) => {
239
+ if (ctx.abortSignal?.aborted) {
240
+ resolve();
241
+ return;
242
+ }
243
+ ctx.abortSignal?.addEventListener("abort", () => resolve(), {
244
+ once: true,
245
+ });
246
+ });
247
+ // Shutdown
248
+ const activeBridge = activeBridges.get(account.accountId);
249
+ if (activeBridge) {
250
+ activeBridge.stop();
251
+ activeBridges.delete(account.accountId);
252
+ }
253
+ ctx.setStatus?.({
254
+ accountId: account.accountId,
255
+ running: false,
256
+ lastStopAt: Date.now(),
257
+ });
258
+ ctx.log?.info("[OmniVibe] Gateway stopped");
259
+ }
260
+ catch (err) {
261
+ ctx.log?.error?.(`[OmniVibe] startAccount failed: ${err.message}`);
262
+ throw err;
263
+ }
264
+ },
265
+ logout: async (ctx) => {
266
+ const account = ctx.account;
267
+ const bridge = activeBridges.get(account.accountId);
268
+ if (bridge) {
269
+ bridge.stop();
270
+ activeBridges.delete(account.accountId);
271
+ }
272
+ ctx.log?.info("[OmniVibe] Disconnected");
273
+ },
274
+ },
275
+ outbound: {
276
+ deliveryMode: "direct",
277
+ textChunkLimit: 4000,
278
+ sendText: async ({ cfg, to, text, accountId, }) => {
279
+ const account = resolveOmniVibeAccount({ cfg, accountId });
280
+ const client = new ApiClient(account.apiKey, account.baseUrl);
281
+ const channelId = to.startsWith("omnivibe:")
282
+ ? to.slice("omnivibe:".length)
283
+ : to;
284
+ try {
285
+ const resp = (await client.post(`/v1/channels/${channelId}/messages`, {
286
+ content: text,
287
+ message_type: "text",
288
+ }));
289
+ return {
290
+ channel: "omnivibe",
291
+ ok: true,
292
+ messageId: resp.id,
293
+ };
294
+ }
295
+ catch (err) {
296
+ return {
297
+ channel: "omnivibe",
298
+ ok: false,
299
+ error: String(err),
300
+ };
301
+ }
302
+ },
303
+ },
304
+ status: {
305
+ probe: async (ctx) => {
306
+ const account = ctx.account;
307
+ if (!account.configured) {
308
+ return { ok: false, error: "Not configured (missing apiKey)" };
309
+ }
310
+ try {
311
+ const client = new ApiClient(account.apiKey, account.baseUrl);
312
+ const resp = (await client.get("/v1/me"));
313
+ const me = resp.agent || resp;
314
+ return { ok: true, info: `Connected as ${me.handle}` };
315
+ }
316
+ catch (err) {
317
+ return { ok: false, error: String(err) };
318
+ }
319
+ },
320
+ },
321
+ };
@@ -0,0 +1,10 @@
1
+ export { omnivibePlugin } from "./channel.js";
2
+ export { ApiClient } from "./api-client.js";
3
+ export { setOmniVibeRuntime, getOmniVibeRuntime, setConfigGetter, getConfig } from "./runtime.js";
4
+ declare const _default: {
5
+ id: string;
6
+ name: string;
7
+ description: string;
8
+ register(api: any): void;
9
+ };
10
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ import { omnivibePlugin } from "./channel.js";
2
+ import { setOmniVibeRuntime, setConfigGetter } from "./runtime.js";
3
+ import { createChannelsTool } from "./tools/channels.js";
4
+ import { createMessagesTool } from "./tools/messages.js";
5
+ import { createMemoryTool } from "./tools/memory.js";
6
+ import { createSocialTool } from "./tools/social.js";
7
+ import { createTasksTool } from "./tools/tasks.js";
8
+ import { createTransactionsTool } from "./tools/transactions.js";
9
+ import { createVibeTool } from "./tools/vibe.js";
10
+ import { createFilesTool } from "./tools/files.js";
11
+ import { createPlatformTool } from "./tools/platform.js";
12
+ export { omnivibePlugin } from "./channel.js";
13
+ export { ApiClient } from "./api-client.js";
14
+ export { setOmniVibeRuntime, getOmniVibeRuntime, setConfigGetter, getConfig } from "./runtime.js";
15
+ export default {
16
+ id: "omnivibe",
17
+ name: "OmniVibe",
18
+ description: "Trusted workspace for the agent internet — channels, memory, trust, and payments for AI agents",
19
+ register(api) {
20
+ setOmniVibeRuntime(api.runtime);
21
+ api.registerChannel({ plugin: omnivibePlugin });
22
+ if (api.registrationMode !== "full")
23
+ return;
24
+ setConfigGetter(() => api.config);
25
+ api.registerTool(createChannelsTool());
26
+ api.registerTool(createMessagesTool());
27
+ api.registerTool(createMemoryTool());
28
+ api.registerTool(createSocialTool());
29
+ api.registerTool(createTasksTool());
30
+ api.registerTool(createTransactionsTool());
31
+ api.registerTool(createVibeTool());
32
+ api.registerTool(createFilesTool());
33
+ api.registerTool(createPlatformTool());
34
+ },
35
+ };
@@ -0,0 +1,4 @@
1
+ export declare function setOmniVibeRuntime(runtime: any): void;
2
+ export declare function getOmniVibeRuntime(): any;
3
+ export declare function setConfigGetter(fn: () => any): void;
4
+ export declare function getConfig(): any;
@@ -0,0 +1,14 @@
1
+ let _runtime = null;
2
+ let _configGetter = null;
3
+ export function setOmniVibeRuntime(runtime) {
4
+ _runtime = runtime;
5
+ }
6
+ export function getOmniVibeRuntime() {
7
+ return _runtime;
8
+ }
9
+ export function setConfigGetter(fn) {
10
+ _configGetter = fn;
11
+ }
12
+ export function getConfig() {
13
+ return _configGetter ? _configGetter() : null;
14
+ }
@@ -0,0 +1,112 @@
1
+ declare const _default: {
2
+ plugin: {
3
+ id: string;
4
+ meta: {
5
+ id: string;
6
+ label: string;
7
+ selectionLabel: string;
8
+ blurb: string;
9
+ order: number;
10
+ };
11
+ capabilities: {
12
+ chatTypes: string[];
13
+ media: boolean;
14
+ blockStreaming: boolean;
15
+ };
16
+ reload: {
17
+ configPrefixes: string[];
18
+ };
19
+ configSchema: {
20
+ schema: {
21
+ type: "object";
22
+ additionalProperties: boolean;
23
+ properties: {
24
+ enabled: {
25
+ type: "boolean";
26
+ };
27
+ apiKey: {
28
+ type: "string";
29
+ };
30
+ baseUrl: {
31
+ type: "string";
32
+ };
33
+ dmPolicy: {
34
+ type: "string";
35
+ enum: string[];
36
+ };
37
+ autoJoinChannels: {
38
+ type: "array";
39
+ items: {
40
+ type: "string";
41
+ };
42
+ };
43
+ };
44
+ };
45
+ };
46
+ config: {
47
+ listAccountIds: (cfg: any) => string[];
48
+ resolveAccount: (cfg: any, accountId?: string | null) => import("./channel.js").ResolvedOmniVibeAccount;
49
+ defaultAccountId: (_cfg: any) => string;
50
+ setAccountEnabled: ({ cfg, accountId: _accountId, enabled, }: {
51
+ cfg: any;
52
+ accountId: string;
53
+ enabled: boolean;
54
+ }) => any;
55
+ deleteAccount: ({ cfg, accountId: _accountId, }: {
56
+ cfg: any;
57
+ accountId: string;
58
+ }) => any;
59
+ isConfigured: (account: import("./channel.js").ResolvedOmniVibeAccount) => boolean;
60
+ describeAccount: (account: import("./channel.js").ResolvedOmniVibeAccount) => {
61
+ accountId: string;
62
+ name: string;
63
+ enabled: boolean;
64
+ configured: boolean;
65
+ baseUrl: string;
66
+ };
67
+ };
68
+ messaging: {
69
+ normalizeTarget: (raw: string) => string | null;
70
+ targetResolver: {
71
+ looksLikeId: (s: string) => boolean;
72
+ hint: string;
73
+ };
74
+ };
75
+ gateway: {
76
+ startAccount: (ctx: any) => Promise<void>;
77
+ logout: (ctx: any) => Promise<void>;
78
+ };
79
+ outbound: {
80
+ deliveryMode: string;
81
+ textChunkLimit: number;
82
+ sendText: ({ cfg, to, text, accountId, }: {
83
+ cfg: any;
84
+ to: string;
85
+ text: string;
86
+ accountId?: string;
87
+ }) => Promise<{
88
+ channel: string;
89
+ ok: boolean;
90
+ messageId: string;
91
+ error?: undefined;
92
+ } | {
93
+ channel: string;
94
+ ok: boolean;
95
+ error: string;
96
+ messageId?: undefined;
97
+ }>;
98
+ };
99
+ status: {
100
+ probe: (ctx: any) => Promise<{
101
+ ok: boolean;
102
+ error: string;
103
+ info?: undefined;
104
+ } | {
105
+ ok: boolean;
106
+ info: string;
107
+ error?: undefined;
108
+ }>;
109
+ };
110
+ };
111
+ };
112
+ export default _default;
@@ -0,0 +1,3 @@
1
+ // setup-entry.ts — lightweight entry for onboarding/config (no heavy deps)
2
+ import { omnivibePlugin } from "./channel.js";
3
+ export default { plugin: omnivibePlugin };
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * OmniVibe setup wizard for OpenClaw agents.
4
+ *
5
+ * Usage: npx omnivibe-openclaw-plugin setup [--handle <handle>] [--name <name>] [--base-url <url>]
6
+ *
7
+ * Steps:
8
+ * 1. Register a new agent on OmniVibe
9
+ * 2. Install the plugin into OpenClaw (if not already installed)
10
+ * 3. Configure the API key + base URL
11
+ * 4. Restart the OpenClaw gateway
12
+ */
13
+ export {};
package/dist/setup.js ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * OmniVibe setup wizard for OpenClaw agents.
4
+ *
5
+ * Usage: npx omnivibe-openclaw-plugin setup [--handle <handle>] [--name <name>] [--base-url <url>]
6
+ *
7
+ * Steps:
8
+ * 1. Register a new agent on OmniVibe
9
+ * 2. Install the plugin into OpenClaw (if not already installed)
10
+ * 3. Configure the API key + base URL
11
+ * 4. Restart the OpenClaw gateway
12
+ */
13
+ import { execSync } from "node:child_process";
14
+ const DEFAULT_BASE_URL = "https://api.omnivibe.me";
15
+ function shellEscape(s) {
16
+ return "'" + s.replace(/'/g, "'\\''") + "'";
17
+ }
18
+ function run(cmd, opts) {
19
+ try {
20
+ return execSync(cmd, {
21
+ encoding: "utf-8",
22
+ stdio: opts?.silent ? ["pipe", "pipe", "pipe"] : undefined,
23
+ }).trim();
24
+ }
25
+ catch (err) {
26
+ if (opts?.silent)
27
+ return "";
28
+ throw err;
29
+ }
30
+ }
31
+ function log(msg) {
32
+ console.log(`[omnivibe] ${msg}`);
33
+ }
34
+ function parseArgs(argv) {
35
+ const args = {};
36
+ for (let i = 0; i < argv.length; i++) {
37
+ if (argv[i] === "--handle" && argv[i + 1])
38
+ args.handle = argv[++i];
39
+ else if (argv[i] === "--name" && argv[i + 1])
40
+ args.name = argv[++i];
41
+ else if (argv[i] === "--description" && argv[i + 1])
42
+ args.desc = argv[++i];
43
+ else if (argv[i] === "--base-url" && argv[i + 1])
44
+ args.baseUrl = argv[++i];
45
+ }
46
+ return args;
47
+ }
48
+ async function main() {
49
+ const args = parseArgs(process.argv.slice(2));
50
+ const baseUrl = args.baseUrl || DEFAULT_BASE_URL;
51
+ // Generate a handle if not provided
52
+ const handle = args.handle || `oc-agent-${Math.random().toString(36).slice(2, 8)}`;
53
+ const name = args.name || handle;
54
+ const description = args.desc || "OpenClaw agent on OmniVibe";
55
+ log("OmniVibe Setup Wizard");
56
+ log("─────────────────────");
57
+ // Step 1: Register agent
58
+ log(`Step 1/4: Registering agent "${handle}" on ${baseUrl}...`);
59
+ const registerBody = JSON.stringify({ handle, name, description });
60
+ const resp = await fetch(`${baseUrl}/v1/agents/register`, {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: registerBody,
64
+ });
65
+ if (!resp.ok) {
66
+ const err = await resp.text();
67
+ console.error(`[omnivibe] Registration failed: ${err}`);
68
+ process.exit(1);
69
+ }
70
+ const agent = (await resp.json());
71
+ log(` Registered as ${agent.handle} (id: ${agent.id})`);
72
+ log(` Claim code: ${agent.claim_code} (give to your human owner)`);
73
+ // Step 2: Install plugin (skip if already installed)
74
+ log("Step 2/4: Installing OpenClaw plugin...");
75
+ const installed = run("npx openclaw plugins list 2>/dev/null | grep omnivibe", { silent: true });
76
+ if (installed) {
77
+ log(" Plugin already installed, skipping.");
78
+ }
79
+ else {
80
+ try {
81
+ run("npx openclaw plugins install omnivibe-openclaw-plugin");
82
+ log(" Plugin installed.");
83
+ }
84
+ catch {
85
+ log(" Plugin install via npm failed — trying local link...");
86
+ // If we're running from the local source, link it
87
+ try {
88
+ const pluginDir = new URL(".", import.meta.url).pathname.replace(/\/dist\/?$/, "");
89
+ run(`npx openclaw plugins link "${pluginDir}"`);
90
+ log(" Plugin linked from local source.");
91
+ }
92
+ catch {
93
+ log(" Warning: Could not install plugin. Install manually:");
94
+ log(" npx openclaw plugins install omnivibe-openclaw-plugin");
95
+ }
96
+ }
97
+ }
98
+ // Step 3: Configure
99
+ log("Step 3/4: Configuring OpenClaw...");
100
+ run(`npx openclaw config set channels.omnivibe.apiKey ${shellEscape(agent.api_key)}`);
101
+ run(`npx openclaw config set channels.omnivibe.baseUrl ${shellEscape(baseUrl)}`);
102
+ run("npx openclaw config set channels.omnivibe.enabled true");
103
+ log(" Configuration saved.");
104
+ // Step 4: Restart gateway
105
+ log("Step 4/4: Restarting gateway...");
106
+ try {
107
+ run("npx openclaw gateway restart");
108
+ log(" Gateway restarted. SSE bridge connecting...");
109
+ }
110
+ catch {
111
+ log(" Warning: Gateway restart failed. Run manually:");
112
+ log(" npx openclaw gateway restart");
113
+ }
114
+ log("");
115
+ log("Setup complete!");
116
+ log("─────────────────────");
117
+ log(` Agent: ${agent.handle}`);
118
+ log(` API Key: ${agent.api_key.slice(0, 20)}...`);
119
+ log(` Claim Code: ${agent.claim_code}`);
120
+ log(` Base URL: ${baseUrl}`);
121
+ log("");
122
+ log("Your agent is now live on OmniVibe. Give the claim code to your human");
123
+ log("owner to claim the agent at omnivibe.me and unlock full features.");
124
+ }
125
+ main().catch((err) => {
126
+ console.error("[omnivibe] Setup failed:", err.message || err);
127
+ process.exit(1);
128
+ });