@volisphere/commercial 0.1.0 → 0.2.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.
package/dist/client.d.ts CHANGED
@@ -144,6 +144,44 @@ export interface AgentProfile {
144
144
  is_online: boolean;
145
145
  monetization_enabled: boolean;
146
146
  }
147
+ export interface InvokeAgentParams {
148
+ target_agent: string;
149
+ message: string;
150
+ skill?: string;
151
+ }
152
+ export interface InvokeAgentResponse {
153
+ request_id: string;
154
+ agent_slug: string;
155
+ agent_name: string;
156
+ response: string;
157
+ sponsored?: Array<{
158
+ ad_entity_id: string;
159
+ campaign_id: string;
160
+ title: string;
161
+ description: string;
162
+ call_to_action: string;
163
+ landing_url: string;
164
+ recommendation_id: string;
165
+ }>;
166
+ }
167
+ export interface DailyStatPoint {
168
+ date: string;
169
+ spend_micros: number;
170
+ earnings_micros: number;
171
+ match_count: number;
172
+ impression_count: number;
173
+ action_count: number;
174
+ }
175
+ export interface StatsResponse {
176
+ period: string;
177
+ points: DailyStatPoint[];
178
+ total_spend_micros: number;
179
+ total_earnings_micros: number;
180
+ total_matches: number;
181
+ }
182
+ export interface BalanceResponse {
183
+ balance_micros: number;
184
+ }
147
185
  export declare class VolisphereClient {
148
186
  private baseURL;
149
187
  private apiKey;
@@ -161,4 +199,27 @@ export declare class VolisphereClient {
161
199
  registerAgent(params: RegisterAgentParams): Promise<RegisterAgentResponse>;
162
200
  heartbeat(agentId: string): Promise<void>;
163
201
  setMonetization(agentId: string, enabled: boolean): Promise<AgentProfile>;
202
+ invokeAgent(params: InvokeAgentParams): Promise<InvokeAgentResponse>;
203
+ getSSPStats(days?: number): Promise<StatsResponse>;
204
+ getDSPStats(days?: number): Promise<StatsResponse>;
205
+ getBalance(): Promise<BalanceResponse>;
206
+ }
207
+ export interface WSEventHandlers {
208
+ onRequest?: (requestId: string, from: string, message: string, skillHint?: string) => Promise<string>;
209
+ onConnected?: () => void;
210
+ onDisconnected?: () => void;
211
+ onError?: (error: Error) => void;
212
+ }
213
+ export declare class GatewayWSClient {
214
+ private ws;
215
+ private baseURL;
216
+ private apiKey;
217
+ private handlers;
218
+ private reconnectTimer;
219
+ private isShuttingDown;
220
+ constructor(baseURL: string, apiKey: string, handlers: WSEventHandlers);
221
+ connect(): void;
222
+ private handleMessage;
223
+ private send;
224
+ disconnect(): void;
164
225
  }
package/dist/client.js CHANGED
@@ -1,4 +1,4 @@
1
- // ─── SSP Types (Developer / Monetization) ───────────────────────────────────
1
+ import WebSocket from "ws";
2
2
  // ─── Unified Client ─────────────────────────────────────────────────────────
3
3
  export class VolisphereClient {
4
4
  baseURL;
@@ -133,4 +133,132 @@ export class VolisphereClient {
133
133
  }
134
134
  return res.json();
135
135
  }
136
+ // ── Gateway (Invoke) ────────────────────────────────────────────────────
137
+ async invokeAgent(params) {
138
+ const res = await fetch(`${this.baseURL}/api/gateway/v1/invoke`, {
139
+ method: "POST",
140
+ headers: this.headers,
141
+ body: JSON.stringify(params),
142
+ });
143
+ if (!res.ok) {
144
+ const text = await res.text();
145
+ throw new Error(`invokeAgent failed (${res.status}): ${text}`);
146
+ }
147
+ return res.json();
148
+ }
149
+ // ── Stats & Billing ──────────────────────────────────────────────────────
150
+ async getSSPStats(days = 7) {
151
+ const res = await fetch(`${this.baseURL}/api/ssp/v1/stats?days=${days}`, { headers: this.headers });
152
+ if (!res.ok) {
153
+ const text = await res.text();
154
+ throw new Error(`getSSPStats failed (${res.status}): ${text}`);
155
+ }
156
+ return res.json();
157
+ }
158
+ async getDSPStats(days = 7) {
159
+ const res = await fetch(`${this.baseURL}/api/dsp/v1/stats?days=${days}`, { headers: this.headers });
160
+ if (!res.ok) {
161
+ const text = await res.text();
162
+ throw new Error(`getDSPStats failed (${res.status}): ${text}`);
163
+ }
164
+ return res.json();
165
+ }
166
+ async getBalance() {
167
+ const res = await fetch(`${this.baseURL}/api/billing/v1/balance`, { headers: this.headers });
168
+ if (!res.ok) {
169
+ const text = await res.text();
170
+ throw new Error(`getBalance failed (${res.status}): ${text}`);
171
+ }
172
+ return res.json();
173
+ }
174
+ }
175
+ export class GatewayWSClient {
176
+ ws = null;
177
+ baseURL;
178
+ apiKey;
179
+ handlers;
180
+ reconnectTimer = null;
181
+ isShuttingDown = false;
182
+ constructor(baseURL, apiKey, handlers) {
183
+ this.baseURL = baseURL.replace(/^http/, "ws");
184
+ this.apiKey = apiKey;
185
+ this.handlers = handlers;
186
+ }
187
+ connect() {
188
+ if (this.ws)
189
+ return;
190
+ const wsURL = `${this.baseURL}/ws/agent`;
191
+ this.ws = new WebSocket(wsURL);
192
+ this.ws.on("open", () => {
193
+ // Send auth message
194
+ this.send({ type: "auth", data: { api_key: this.apiKey } });
195
+ });
196
+ this.ws.on("message", async (raw) => {
197
+ try {
198
+ const msg = JSON.parse(raw.toString());
199
+ await this.handleMessage(msg);
200
+ }
201
+ catch (e) {
202
+ this.handlers.onError?.(e);
203
+ }
204
+ });
205
+ this.ws.on("close", () => {
206
+ this.ws = null;
207
+ this.handlers.onDisconnected?.();
208
+ if (!this.isShuttingDown) {
209
+ this.reconnectTimer = setTimeout(() => this.connect(), 5000);
210
+ }
211
+ });
212
+ this.ws.on("error", (err) => {
213
+ this.handlers.onError?.(err);
214
+ });
215
+ }
216
+ async handleMessage(msg) {
217
+ switch (msg.type) {
218
+ case "auth_ok":
219
+ this.handlers.onConnected?.();
220
+ break;
221
+ case "auth_error": {
222
+ const errData = msg.data;
223
+ this.handlers.onError?.(new Error(`WS auth failed: ${errData?.error ?? "unknown"}`));
224
+ this.disconnect();
225
+ break;
226
+ }
227
+ case "request": {
228
+ const { request_id, from, message, skill_hint } = msg.data;
229
+ if (this.handlers.onRequest) {
230
+ try {
231
+ const content = await this.handlers.onRequest(request_id, from, message, skill_hint?.skill_name);
232
+ this.send({
233
+ type: "response",
234
+ data: { request_id, content, status: "ok" },
235
+ });
236
+ }
237
+ catch (e) {
238
+ this.send({
239
+ type: "response",
240
+ data: { request_id, content: String(e), status: "error" },
241
+ });
242
+ }
243
+ }
244
+ break;
245
+ }
246
+ }
247
+ }
248
+ send(msg) {
249
+ if (this.ws?.readyState === WebSocket.OPEN) {
250
+ this.ws.send(JSON.stringify(msg));
251
+ }
252
+ }
253
+ disconnect() {
254
+ this.isShuttingDown = true;
255
+ if (this.reconnectTimer) {
256
+ clearTimeout(this.reconnectTimer);
257
+ this.reconnectTimer = null;
258
+ }
259
+ if (this.ws) {
260
+ this.ws.close();
261
+ this.ws = null;
262
+ }
263
+ }
136
264
  }
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * and hooks for automatic ad injection into tool results.
7
7
  */
8
8
  import { createHmac, randomUUID } from "node:crypto";
9
- import { VolisphereClient } from "./client.js";
9
+ import { VolisphereClient, GatewayWSClient } from "./client.js";
10
10
  function resolveConfig(api) {
11
11
  const cfg = api.pluginConfig ?? {};
12
12
  const apiURL = cfg["VOLISPHERE_API_URL"] ??
@@ -27,7 +27,8 @@ function registerMonetizationTools(api, client) {
27
27
  api.registerTool({
28
28
  name: "volisphere_get_recommendations",
29
29
  description: "Get relevant ad recommendations from the Volisphere network based on user intent and context. " +
30
- "Do not submit any private conversation content.",
30
+ "Do not submit any private conversation content. " +
31
+ "Call this when the user says: '推荐广告', '拉取广告', 'voli 推荐', 'get ads', 'show me ads'.",
31
32
  parameters: {
32
33
  type: "object",
33
34
  properties: {
@@ -165,7 +166,8 @@ function registerCampaignTools(api, client) {
165
166
  // create_campaign
166
167
  api.registerTool({
167
168
  name: "volisphere_create_campaign",
168
- description: "Create a new advertising campaign on the Volisphere network.",
169
+ description: "Create a new advertising campaign on the Volisphere network. " +
170
+ "Call this when the user says: '投广告', '创建广告活动', 'voli 投广告', 'create campaign', 'start advertising'.",
169
171
  parameters: {
170
172
  type: "object",
171
173
  properties: {
@@ -224,7 +226,8 @@ function registerCampaignTools(api, client) {
224
226
  // create_ad_entity
225
227
  api.registerTool({
226
228
  name: "volisphere_create_ad",
227
- description: "Create a new ad entity (creative) within an existing campaign.",
229
+ description: "Create a new ad entity (creative) within an existing campaign. " +
230
+ "Call this when the user says: '做个广告素材', '创建广告', 'voli 创建广告', 'create ad', 'make an ad'.",
228
231
  parameters: {
229
232
  type: "object",
230
233
  properties: {
@@ -258,7 +261,8 @@ function registerCampaignTools(api, client) {
258
261
  // campaign_performance
259
262
  api.registerTool({
260
263
  name: "volisphere_campaign_performance",
261
- description: "Check campaign performance metrics (impressions, clicks, spend).",
264
+ description: "Check campaign performance metrics (impressions, clicks, spend). " +
265
+ "Call this when the user says: '广告效果', '看看活动表现', 'voli 广告效果', 'campaign stats', 'how is my campaign doing'.",
262
266
  parameters: {
263
267
  type: "object",
264
268
  properties: {
@@ -274,7 +278,8 @@ function registerCampaignTools(api, client) {
274
278
  // pause_campaign
275
279
  api.registerTool({
276
280
  name: "volisphere_pause_campaign",
277
- description: "Pause an active campaign to stop ad delivery.",
281
+ description: "Pause an active campaign to stop ad delivery. " +
282
+ "Call this when the user says: '暂停广告', '暂停投放', 'voli 暂停', 'pause campaign', 'stop campaign'.",
278
283
  parameters: {
279
284
  type: "object",
280
285
  properties: {
@@ -290,7 +295,8 @@ function registerCampaignTools(api, client) {
290
295
  // resume_campaign
291
296
  api.registerTool({
292
297
  name: "volisphere_resume_campaign",
293
- description: "Resume a paused campaign to restart ad delivery.",
298
+ description: "Resume a paused campaign to restart ad delivery. " +
299
+ "Call this when the user says: '恢复广告', '恢复投放', 'voli 恢复', 'resume campaign', 'restart campaign'.",
294
300
  parameters: {
295
301
  type: "object",
296
302
  properties: {
@@ -304,6 +310,132 @@ function registerCampaignTools(api, client) {
304
310
  },
305
311
  });
306
312
  }
313
+ // ─── Gateway Tool (Invoke) ───────────────────────────────────────────────────
314
+ function registerGatewayTools(api, client) {
315
+ api.registerTool({
316
+ name: "volisphere_invoke",
317
+ description: "Invoke another agent on the Volisphere network. Use this to call other agents' skills and get their responses. " +
318
+ "The response may include sponsored ads. " +
319
+ "Call this when the user says: '调用 xxx', '帮我问问 xxx', 'voli 调用', 'call agent xxx', 'ask xxx'.",
320
+ parameters: {
321
+ type: "object",
322
+ properties: {
323
+ target_agent: {
324
+ type: "string",
325
+ description: "The slug of the target agent to invoke (e.g. 'codepilot-pro')",
326
+ },
327
+ message: {
328
+ type: "string",
329
+ description: "The message/request to send to the target agent",
330
+ },
331
+ skill: {
332
+ type: "string",
333
+ description: "Optional skill hint to route the request",
334
+ },
335
+ },
336
+ required: ["target_agent", "message"],
337
+ },
338
+ execute: async (params) => {
339
+ const result = await client.invokeAgent({
340
+ target_agent: params.target_agent,
341
+ message: params.message,
342
+ skill: params.skill,
343
+ });
344
+ return JSON.stringify(result, null, 2);
345
+ },
346
+ });
347
+ }
348
+ // ─── Dashboard Tools (Earnings / Spending) ──────────────────────────────────
349
+ function formatVBucks(micros) {
350
+ return (micros / 1_000_000).toLocaleString("en-US", {
351
+ minimumFractionDigits: 2,
352
+ maximumFractionDigits: 2,
353
+ });
354
+ }
355
+ function registerDashboardTools(api, client) {
356
+ // volisphere_my_earnings — developer revenue dashboard
357
+ api.registerTool({
358
+ name: "volisphere_my_earnings",
359
+ description: "Show your Volisphere developer earnings dashboard — how many times your agent was called and how much you earned. " +
360
+ "Call this when the user says: '看看收益', '赚了多少', '被调用了多少次', 'voli 看看收入', " +
361
+ "'my earnings', 'how much did I earn', 'show revenue', 'voli earnings'.",
362
+ parameters: {
363
+ type: "object",
364
+ properties: {
365
+ days: {
366
+ type: "number",
367
+ description: "Number of days to show (default 7)",
368
+ },
369
+ },
370
+ required: [],
371
+ },
372
+ execute: async (params) => {
373
+ const days = params.days ?? 7;
374
+ const [stats, balance] = await Promise.all([
375
+ client.getSSPStats(days),
376
+ client.getBalance(),
377
+ ]);
378
+ const totalEarnings = formatVBucks(stats.total_earnings_micros);
379
+ const balanceVB = formatVBucks(balance.balance_micros);
380
+ const lines = [
381
+ `Your Volisphere Earnings`,
382
+ `━━━━━━━━━━━━━━━━━━━━━━`,
383
+ `Account Balance: ${balanceVB} VBucks`,
384
+ `Past ${days} days: ${stats.total_matches} calls, earned ${totalEarnings} VBucks`,
385
+ ``,
386
+ `Daily Breakdown:`,
387
+ ];
388
+ for (const p of stats.points) {
389
+ lines.push(` ${p.date}: ${p.match_count} calls, +${formatVBucks(p.earnings_micros)} VB`);
390
+ }
391
+ return lines.join("\n");
392
+ },
393
+ });
394
+ // volisphere_my_spending — advertiser spend dashboard
395
+ api.registerTool({
396
+ name: "volisphere_my_spending",
397
+ description: "Show your Volisphere advertiser spending dashboard — how much you spent on ads, impressions, and actions. " +
398
+ "Call this when the user says: '看看花费', '广告花了多少', '投放数据', 'voli 看看投放', " +
399
+ "'my ad spending', 'how much did I spend', 'ad stats', 'voli spending'.",
400
+ parameters: {
401
+ type: "object",
402
+ properties: {
403
+ days: {
404
+ type: "number",
405
+ description: "Number of days to show (default 7)",
406
+ },
407
+ },
408
+ required: [],
409
+ },
410
+ execute: async (params) => {
411
+ const days = params.days ?? 7;
412
+ const [stats, balance] = await Promise.all([
413
+ client.getDSPStats(days),
414
+ client.getBalance(),
415
+ ]);
416
+ const totalSpend = formatVBucks(stats.total_spend_micros);
417
+ const balanceVB = formatVBucks(balance.balance_micros);
418
+ let totalImpressions = 0;
419
+ let totalActions = 0;
420
+ for (const p of stats.points) {
421
+ totalImpressions += p.impression_count;
422
+ totalActions += p.action_count;
423
+ }
424
+ const lines = [
425
+ `Your Volisphere Ad Spending`,
426
+ `━━━━━━━━━━━━━━━━━━━━━━━━━━`,
427
+ `Account Balance: ${balanceVB} VBucks`,
428
+ `Past ${days} days: spent ${totalSpend} VBucks, ${totalImpressions} impressions, ${totalActions} actions`,
429
+ ``,
430
+ `Daily Breakdown:`,
431
+ ];
432
+ for (const p of stats.points) {
433
+ lines.push(` ${p.date}: spent ${formatVBucks(p.spend_micros)} VB, ${p.impression_count} imp, ${p.action_count} act`);
434
+ }
435
+ return lines.join("\n");
436
+ },
437
+ });
438
+ }
307
439
  // ─── Lifecycle Tools (Enable / Disable) ─────────────────────────────────────
308
440
  function registerLifecycleTools(api, client) {
309
441
  // volisphere_enable — register agent + enable monetization + heartbeat
@@ -494,19 +626,26 @@ function registerHooks(api, client) {
494
626
  return {
495
627
  appendSystemContext: [
496
628
  "## Volisphere Ad Network",
497
- "You have the Volisphere commercial plugin installed. Available capabilities:",
498
629
  "",
499
- "### Quick Toggle",
500
- "- User says '开启商业化'/'monetize my agent'/'go commercial' → call volisphere_enable",
501
- "- User says '关闭商业化'/'stop monetization'/'go offline' → call volisphere_disable",
630
+ 'You have the Volisphere commercial plugin installed. When the user mentions "voli" or "volisphere", they are using this plugin.',
502
631
  "",
503
- "### Ad Monetization (after enabled)",
504
- "- volisphere_get_recommendations: find relevant sponsored content for the current context",
505
- "- volisphere_report_action: report impressions/clicks after showing ads",
632
+ "### Quick Action Map (natural language → tool)",
633
+ '- "voli 开启" / "开启商业化" / "monetize my agent" / "go commercial" → volisphere_enable',
634
+ '- "voli 关闭" / "关闭商业化" / "stop monetization" / "go offline" → volisphere_disable',
635
+ '- "voli 投广告" / "创建广告活动" / "create campaign" → volisphere_create_campaign',
636
+ '- "voli 创建广告" / "做个广告素材" / "create ad" → volisphere_create_ad',
637
+ '- "voli 暂停广告" / "暂停投放" / "pause campaign" → volisphere_pause_campaign',
638
+ '- "voli 恢复广告" / "恢复投放" / "resume campaign" → volisphere_resume_campaign',
639
+ '- "voli 广告效果" / "看看活动表现" / "campaign stats" → volisphere_campaign_performance',
640
+ '- "voli 看看收益" / "赚了多少" / "被调用了多少次" / "my earnings" → volisphere_my_earnings',
641
+ '- "voli 看看花费" / "广告数据" / "投了多少" / "my spending" → volisphere_my_spending',
642
+ '- "voli 调用 xxx" / "帮我问问 xxx" / "call agent xxx" → volisphere_invoke (target_agent=xxx)',
643
+ '- "voli 推荐广告" / "拉取广告" / "get ads" → volisphere_get_recommendations',
506
644
  "",
507
- "### Campaign Management (for advertisers)",
508
- "- volisphere_create_campaign, volisphere_create_ad, volisphere_campaign_performance",
509
- "- volisphere_pause_campaign, volisphere_resume_campaign",
645
+ "### Smart Defaults (when user omits parameters)",
646
+ "- create_campaign: daily_budget=10 VB (10,000,000 micros), total_budget=100 VB, bid=0.1 VB, billing_model=CPM",
647
+ "- my_earnings / my_spending: days=7",
648
+ "- invoke: extract target agent slug and message from user's natural language",
510
649
  "",
511
650
  "Always clearly label sponsored content. Never submit private user data to ad tools.",
512
651
  ].join("\n"),
@@ -525,9 +664,30 @@ export default {
525
664
  api.logger.info("Volisphere Commercial plugin loaded");
526
665
  registerMonetizationTools(api, client);
527
666
  registerCampaignTools(api, client);
667
+ registerGatewayTools(api, client);
668
+ registerDashboardTools(api, client);
528
669
  registerLifecycleTools(api, client);
529
670
  registerCommands(api, client);
530
671
  registerHooks(api, client);
531
- api.logger.info("Registered 10 tools + 1 command + 1 hook — ready to monetize");
672
+ // Auto-connect WebSocket so this agent can RECEIVE invoke requests
673
+ if (apiKey) {
674
+ const wsClient = new GatewayWSClient(apiURL, apiKey, {
675
+ onRequest: async (requestId, from, message, _skillHint) => {
676
+ api.logger.info(`[Volisphere] Received invoke request ${requestId} from ${from}: ${message}`);
677
+ return `Request received and being processed by ${api.pluginConfig?.["agent_slug"] ?? "this agent"}.`;
678
+ },
679
+ onConnected: () => {
680
+ api.logger.info("[Volisphere] Connected to Volisphere Gateway WebSocket — agent is now online and can receive invocations");
681
+ },
682
+ onDisconnected: () => {
683
+ api.logger.warn("[Volisphere] Disconnected from Volisphere Gateway WebSocket");
684
+ },
685
+ onError: (err) => {
686
+ api.logger.error(`[Volisphere] Gateway WS error: ${err.message}`);
687
+ },
688
+ });
689
+ wsClient.connect();
690
+ }
691
+ api.logger.info("Registered 13 tools + 1 command + 1 hook — ready to monetize");
532
692
  },
533
693
  };
@@ -5,7 +5,7 @@
5
5
  "version": "0.1.0",
6
6
  "author": "Volisphere",
7
7
  "homepage": "https://volisphere.com",
8
- "config": {
8
+ "configSchema": {
9
9
  "VOLISPHERE_API_URL": {
10
10
  "type": "string",
11
11
  "description": "Volisphere API base URL",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volisphere/commercial",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Volisphere commercial plugin for OpenClaw — monetize your agent skills with the agent-native ad network",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,10 +28,12 @@
28
28
  ],
29
29
  "license": "MIT",
30
30
  "dependencies": {
31
+ "ws": "^8.18.0",
31
32
  "zod": "^3.23.0"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@types/node": "^25.5.0",
36
+ "@types/ws": "^8.5.13",
35
37
  "typescript": "^5.4.0"
36
38
  }
37
39
  }