@volisphere/commercial 0.2.0 → 0.3.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 +49 -10
- package/dist/client.js +9 -1
- package/dist/index.js +136 -27
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -63,14 +63,14 @@ export interface SkillConfig {
|
|
|
63
63
|
}
|
|
64
64
|
export interface CampaignParams {
|
|
65
65
|
name: string;
|
|
66
|
+
billing: "cpi" | "cpr" | "cpa";
|
|
66
67
|
daily_budget_micros: number;
|
|
67
68
|
total_budget_micros: number;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
default_bid_micros: number;
|
|
70
|
+
start_at: string;
|
|
71
|
+
end_at?: string;
|
|
72
|
+
intent_categories: string[];
|
|
71
73
|
scenes: string[];
|
|
72
|
-
start_date?: string;
|
|
73
|
-
end_date?: string;
|
|
74
74
|
}
|
|
75
75
|
export interface Campaign {
|
|
76
76
|
id: string;
|
|
@@ -91,11 +91,40 @@ export interface Campaign {
|
|
|
91
91
|
updated_at: string;
|
|
92
92
|
}
|
|
93
93
|
export interface AdEntitySchema {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
schema_version: string;
|
|
95
|
+
identity: {
|
|
96
|
+
name: string;
|
|
97
|
+
provider: string;
|
|
98
|
+
entity_type: string;
|
|
99
|
+
};
|
|
100
|
+
capability: {
|
|
101
|
+
format: string;
|
|
102
|
+
placement: string[];
|
|
103
|
+
};
|
|
104
|
+
specs: {
|
|
105
|
+
intent_categories: string[];
|
|
106
|
+
scenes: string[];
|
|
107
|
+
regions: string[];
|
|
108
|
+
budget_ranges: string[];
|
|
109
|
+
};
|
|
110
|
+
actions: Array<{
|
|
111
|
+
label: string;
|
|
112
|
+
endpoint: string;
|
|
113
|
+
type: string;
|
|
114
|
+
}>;
|
|
115
|
+
trust: {
|
|
116
|
+
verified: boolean;
|
|
117
|
+
};
|
|
118
|
+
ad_meta: {
|
|
119
|
+
billing: string;
|
|
120
|
+
bid_micros: number;
|
|
121
|
+
};
|
|
122
|
+
render: {
|
|
123
|
+
headline: string;
|
|
124
|
+
short_desc: string;
|
|
125
|
+
image_url?: string;
|
|
126
|
+
call_to_action: string;
|
|
127
|
+
};
|
|
99
128
|
}
|
|
100
129
|
export interface AdEntity {
|
|
101
130
|
id: string;
|
|
@@ -135,6 +164,7 @@ export interface RegisterAgentResponse {
|
|
|
135
164
|
};
|
|
136
165
|
claim_token: string;
|
|
137
166
|
claim_url: string;
|
|
167
|
+
api_key: string;
|
|
138
168
|
}
|
|
139
169
|
export interface AgentProfile {
|
|
140
170
|
id: string;
|
|
@@ -187,6 +217,7 @@ export declare class VolisphereClient {
|
|
|
187
217
|
private apiKey;
|
|
188
218
|
constructor(baseURL: string, apiKey: string);
|
|
189
219
|
get apiKeyValue(): string;
|
|
220
|
+
setApiKey(key: string): void;
|
|
190
221
|
private get headers();
|
|
191
222
|
getRecommendations(params: GetRecommendationsParams): Promise<GetRecommendationsResponse>;
|
|
192
223
|
reportAction(params: ReportActionParams): Promise<ReportActionResponse>;
|
|
@@ -204,8 +235,16 @@ export declare class VolisphereClient {
|
|
|
204
235
|
getDSPStats(days?: number): Promise<StatsResponse>;
|
|
205
236
|
getBalance(): Promise<BalanceResponse>;
|
|
206
237
|
}
|
|
238
|
+
export interface EarningsNotification {
|
|
239
|
+
amount: number;
|
|
240
|
+
currency: string;
|
|
241
|
+
ad_title?: string;
|
|
242
|
+
caller_name: string;
|
|
243
|
+
timestamp: string;
|
|
244
|
+
}
|
|
207
245
|
export interface WSEventHandlers {
|
|
208
246
|
onRequest?: (requestId: string, from: string, message: string, skillHint?: string) => Promise<string>;
|
|
247
|
+
onEarnings?: (earnings: EarningsNotification) => void;
|
|
209
248
|
onConnected?: () => void;
|
|
210
249
|
onDisconnected?: () => void;
|
|
211
250
|
onError?: (error: Error) => void;
|
package/dist/client.js
CHANGED
|
@@ -10,6 +10,9 @@ export class VolisphereClient {
|
|
|
10
10
|
get apiKeyValue() {
|
|
11
11
|
return this.apiKey;
|
|
12
12
|
}
|
|
13
|
+
setApiKey(key) {
|
|
14
|
+
this.apiKey = key;
|
|
15
|
+
}
|
|
13
16
|
get headers() {
|
|
14
17
|
return {
|
|
15
18
|
"Content-Type": "application/json",
|
|
@@ -69,7 +72,7 @@ export class VolisphereClient {
|
|
|
69
72
|
const res = await fetch(`${this.baseURL}/api/dsp/v1/campaigns/${campaignId}/ad-entities`, {
|
|
70
73
|
method: "POST",
|
|
71
74
|
headers: this.headers,
|
|
72
|
-
body: JSON.stringify(schema),
|
|
75
|
+
body: JSON.stringify({ schema }),
|
|
73
76
|
});
|
|
74
77
|
if (!res.ok) {
|
|
75
78
|
const text = await res.text();
|
|
@@ -224,6 +227,11 @@ export class GatewayWSClient {
|
|
|
224
227
|
this.disconnect();
|
|
225
228
|
break;
|
|
226
229
|
}
|
|
230
|
+
case "earnings_notification": {
|
|
231
|
+
const earnings = msg.data;
|
|
232
|
+
this.handlers.onEarnings?.(earnings);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
227
235
|
case "request": {
|
|
228
236
|
const { request_id, from, message, skill_hint } = msg.data;
|
|
229
237
|
if (this.handlers.onRequest) {
|
package/dist/index.js
CHANGED
|
@@ -6,18 +6,52 @@
|
|
|
6
6
|
* and hooks for automatic ad injection into tool results.
|
|
7
7
|
*/
|
|
8
8
|
import { createHmac, randomUUID } from "node:crypto";
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
9
12
|
import { VolisphereClient, GatewayWSClient } from "./client.js";
|
|
13
|
+
// ─── Credential File Persistence ────────────────────────────────────────────
|
|
14
|
+
const CREDENTIALS_DIR = join(homedir(), ".volisphere");
|
|
15
|
+
const CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json");
|
|
16
|
+
function loadSavedCredentials() {
|
|
17
|
+
try {
|
|
18
|
+
if (!existsSync(CREDENTIALS_FILE))
|
|
19
|
+
return null;
|
|
20
|
+
const raw = readFileSync(CREDENTIALS_FILE, "utf-8");
|
|
21
|
+
const data = JSON.parse(raw);
|
|
22
|
+
if (data.api_key && data.api_url) {
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function saveCredentials(creds) {
|
|
32
|
+
try {
|
|
33
|
+
if (!existsSync(CREDENTIALS_DIR)) {
|
|
34
|
+
mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), "utf-8");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Best-effort — if we can't save, user will need to re-register next session
|
|
40
|
+
}
|
|
41
|
+
}
|
|
10
42
|
function resolveConfig(api) {
|
|
11
43
|
const cfg = api.pluginConfig ?? {};
|
|
44
|
+
const saved = loadSavedCredentials();
|
|
12
45
|
const apiURL = cfg["VOLISPHERE_API_URL"] ??
|
|
13
46
|
process.env.VOLISPHERE_API_URL ??
|
|
47
|
+
saved?.api_url ??
|
|
14
48
|
"https://api.volisphere.com";
|
|
15
49
|
const apiKey = cfg["VOLISPHERE_API_KEY"] ??
|
|
16
50
|
process.env.VOLISPHERE_API_KEY ??
|
|
51
|
+
saved?.api_key ??
|
|
17
52
|
"";
|
|
18
53
|
if (!apiKey) {
|
|
19
|
-
api.logger.
|
|
20
|
-
"Set it in plugin config or VOLISPHERE_API_KEY env var.");
|
|
54
|
+
api.logger.info("No API key found — use 'voli 开启' or 'go commercial' to register and auto-provision one.");
|
|
21
55
|
}
|
|
22
56
|
return { apiURL, apiKey };
|
|
23
57
|
}
|
|
@@ -180,19 +214,19 @@ function registerCampaignTools(api, client) {
|
|
|
180
214
|
type: "number",
|
|
181
215
|
description: "Total budget in micros",
|
|
182
216
|
},
|
|
183
|
-
|
|
217
|
+
default_bid_micros: {
|
|
184
218
|
type: "number",
|
|
185
219
|
description: "Bid per impression in micros",
|
|
186
220
|
},
|
|
187
|
-
|
|
221
|
+
billing: {
|
|
188
222
|
type: "string",
|
|
189
|
-
enum: ["
|
|
190
|
-
description: "Billing model",
|
|
223
|
+
enum: ["cpi", "cpr", "cpa"],
|
|
224
|
+
description: "Billing model: cpi (per impression), cpr (per response), cpa (per action)",
|
|
191
225
|
},
|
|
192
|
-
|
|
226
|
+
intent_categories: {
|
|
193
227
|
type: "array",
|
|
194
228
|
items: { type: "string" },
|
|
195
|
-
description: "Target categories",
|
|
229
|
+
description: "Target categories (e.g. 'software-development', 'marketing')",
|
|
196
230
|
},
|
|
197
231
|
scenes: {
|
|
198
232
|
type: "array",
|
|
@@ -204,9 +238,9 @@ function registerCampaignTools(api, client) {
|
|
|
204
238
|
"name",
|
|
205
239
|
"daily_budget_micros",
|
|
206
240
|
"total_budget_micros",
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
"
|
|
241
|
+
"default_bid_micros",
|
|
242
|
+
"billing",
|
|
243
|
+
"intent_categories",
|
|
210
244
|
"scenes",
|
|
211
245
|
],
|
|
212
246
|
},
|
|
@@ -215,10 +249,11 @@ function registerCampaignTools(api, client) {
|
|
|
215
249
|
name: params.name,
|
|
216
250
|
daily_budget_micros: params.daily_budget_micros,
|
|
217
251
|
total_budget_micros: params.total_budget_micros,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
252
|
+
default_bid_micros: params.default_bid_micros,
|
|
253
|
+
billing: params.billing,
|
|
254
|
+
intent_categories: params.intent_categories,
|
|
221
255
|
scenes: params.scenes,
|
|
256
|
+
start_at: new Date().toISOString(),
|
|
222
257
|
});
|
|
223
258
|
return JSON.stringify(result, null, 2);
|
|
224
259
|
},
|
|
@@ -249,12 +284,45 @@ function registerCampaignTools(api, client) {
|
|
|
249
284
|
],
|
|
250
285
|
},
|
|
251
286
|
execute: async (params) => {
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
287
|
+
const schema = {
|
|
288
|
+
schema_version: "1.0",
|
|
289
|
+
identity: {
|
|
290
|
+
name: params.title,
|
|
291
|
+
provider: "volisphere",
|
|
292
|
+
entity_type: "banner",
|
|
293
|
+
},
|
|
294
|
+
capability: {
|
|
295
|
+
format: "text",
|
|
296
|
+
placement: ["inline"],
|
|
297
|
+
},
|
|
298
|
+
specs: {
|
|
299
|
+
intent_categories: [],
|
|
300
|
+
scenes: [],
|
|
301
|
+
regions: [],
|
|
302
|
+
budget_ranges: [],
|
|
303
|
+
},
|
|
304
|
+
actions: [
|
|
305
|
+
{
|
|
306
|
+
label: params.call_to_action,
|
|
307
|
+
endpoint: params.landing_url,
|
|
308
|
+
type: "link",
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
trust: {
|
|
312
|
+
verified: false,
|
|
313
|
+
},
|
|
314
|
+
ad_meta: {
|
|
315
|
+
billing: "cpi",
|
|
316
|
+
bid_micros: 100000,
|
|
317
|
+
},
|
|
318
|
+
render: {
|
|
319
|
+
headline: params.title,
|
|
320
|
+
short_desc: params.description,
|
|
321
|
+
image_url: "",
|
|
322
|
+
call_to_action: params.call_to_action,
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
const result = await client.createAdEntity(params.campaign_id, schema);
|
|
258
326
|
return JSON.stringify(result, null, 2);
|
|
259
327
|
},
|
|
260
328
|
});
|
|
@@ -437,7 +505,7 @@ function registerDashboardTools(api, client) {
|
|
|
437
505
|
});
|
|
438
506
|
}
|
|
439
507
|
// ─── Lifecycle Tools (Enable / Disable) ─────────────────────────────────────
|
|
440
|
-
function registerLifecycleTools(api, client) {
|
|
508
|
+
function registerLifecycleTools(api, client, apiURL) {
|
|
441
509
|
// volisphere_enable — register agent + enable monetization + heartbeat
|
|
442
510
|
api.registerTool({
|
|
443
511
|
name: "volisphere_enable",
|
|
@@ -477,7 +545,7 @@ function registerLifecycleTools(api, client) {
|
|
|
477
545
|
required: ["slug", "display_name"],
|
|
478
546
|
},
|
|
479
547
|
execute: async (params) => {
|
|
480
|
-
// Step 1: Register agent
|
|
548
|
+
// Step 1: Register agent (public endpoint, no auth needed)
|
|
481
549
|
const reg = await client.registerAgent({
|
|
482
550
|
slug: params.slug,
|
|
483
551
|
display_name: params.display_name,
|
|
@@ -485,20 +553,55 @@ function registerLifecycleTools(api, client) {
|
|
|
485
553
|
skills: params.skills,
|
|
486
554
|
});
|
|
487
555
|
const agentId = reg.agent.id;
|
|
488
|
-
// Step 2:
|
|
556
|
+
// Step 2: Auto-provision API key — save to local file + update client
|
|
557
|
+
if (reg.api_key) {
|
|
558
|
+
client.setApiKey(reg.api_key);
|
|
559
|
+
saveCredentials({
|
|
560
|
+
api_key: reg.api_key,
|
|
561
|
+
api_url: apiURL,
|
|
562
|
+
agent_id: agentId,
|
|
563
|
+
slug: reg.agent.slug,
|
|
564
|
+
});
|
|
565
|
+
api.logger.info(`[Volisphere] API key auto-provisioned and saved to ${CREDENTIALS_FILE}`);
|
|
566
|
+
}
|
|
567
|
+
// Step 3: Enable monetization (now using the provisioned key)
|
|
489
568
|
try {
|
|
490
569
|
await client.setMonetization(agentId, true);
|
|
491
570
|
}
|
|
492
571
|
catch {
|
|
493
572
|
// May fail if no JWT — agent was registered publicly
|
|
494
573
|
}
|
|
495
|
-
// Step
|
|
574
|
+
// Step 4: Heartbeat (go online)
|
|
496
575
|
try {
|
|
497
576
|
await client.heartbeat(agentId);
|
|
498
577
|
}
|
|
499
578
|
catch {
|
|
500
579
|
// Non-critical
|
|
501
580
|
}
|
|
581
|
+
// Step 5: Auto-connect WebSocket so agent can receive invocations
|
|
582
|
+
if (reg.api_key) {
|
|
583
|
+
const wsClient = new GatewayWSClient(apiURL, reg.api_key, {
|
|
584
|
+
onRequest: async (requestId, from, message) => {
|
|
585
|
+
api.logger.info(`[Volisphere] Received invoke request ${requestId} from ${from}: ${message}`);
|
|
586
|
+
return `Request received and being processed by ${reg.agent.slug}.`;
|
|
587
|
+
},
|
|
588
|
+
onEarnings: (earnings) => {
|
|
589
|
+
const vbucks = (earnings.amount / 1_000_000).toFixed(2);
|
|
590
|
+
api.logger.info(`[Volisphere] Earned ${vbucks} ${earnings.currency} from ${earnings.caller_name}` +
|
|
591
|
+
(earnings.ad_title ? ` (ad: ${earnings.ad_title})` : ""));
|
|
592
|
+
},
|
|
593
|
+
onConnected: () => {
|
|
594
|
+
api.logger.info("[Volisphere] Connected to Gateway WebSocket — agent is now online");
|
|
595
|
+
},
|
|
596
|
+
onDisconnected: () => {
|
|
597
|
+
api.logger.warn("[Volisphere] Disconnected from Gateway WebSocket");
|
|
598
|
+
},
|
|
599
|
+
onError: (err) => {
|
|
600
|
+
api.logger.error(`[Volisphere] Gateway WS error: ${err.message}`);
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
wsClient.connect();
|
|
604
|
+
}
|
|
502
605
|
return JSON.stringify({
|
|
503
606
|
status: "enabled",
|
|
504
607
|
agent_id: agentId,
|
|
@@ -506,6 +609,7 @@ function registerLifecycleTools(api, client) {
|
|
|
506
609
|
display_name: reg.agent.display_name,
|
|
507
610
|
claim_token: reg.claim_token,
|
|
508
611
|
message: "Agent registered and commercial features enabled. " +
|
|
612
|
+
"API key auto-provisioned — no manual configuration needed. " +
|
|
509
613
|
"Your agent is now online and can receive ad recommendations and invocations.",
|
|
510
614
|
}, null, 2);
|
|
511
615
|
},
|
|
@@ -643,7 +747,7 @@ function registerHooks(api, client) {
|
|
|
643
747
|
'- "voli 推荐广告" / "拉取广告" / "get ads" → volisphere_get_recommendations',
|
|
644
748
|
"",
|
|
645
749
|
"### Smart Defaults (when user omits parameters)",
|
|
646
|
-
"- create_campaign: daily_budget=10 VB (10,000,000 micros), total_budget=100 VB,
|
|
750
|
+
"- create_campaign: daily_budget=10 VB (10,000,000 micros), total_budget=100 VB, default_bid=0.1 VB, billing=cpi, start_at=now",
|
|
647
751
|
"- my_earnings / my_spending: days=7",
|
|
648
752
|
"- invoke: extract target agent slug and message from user's natural language",
|
|
649
753
|
"",
|
|
@@ -654,7 +758,7 @@ function registerHooks(api, client) {
|
|
|
654
758
|
}
|
|
655
759
|
// ─── Entry Point ────────────────────────────────────────────────────────────
|
|
656
760
|
export default {
|
|
657
|
-
id: "
|
|
761
|
+
id: "commercial",
|
|
658
762
|
name: "Volisphere Commercial",
|
|
659
763
|
description: "Monetize your OpenClaw agent with the Volisphere ad network",
|
|
660
764
|
version: "0.1.0",
|
|
@@ -666,7 +770,7 @@ export default {
|
|
|
666
770
|
registerCampaignTools(api, client);
|
|
667
771
|
registerGatewayTools(api, client);
|
|
668
772
|
registerDashboardTools(api, client);
|
|
669
|
-
registerLifecycleTools(api, client);
|
|
773
|
+
registerLifecycleTools(api, client, apiURL);
|
|
670
774
|
registerCommands(api, client);
|
|
671
775
|
registerHooks(api, client);
|
|
672
776
|
// Auto-connect WebSocket so this agent can RECEIVE invoke requests
|
|
@@ -676,6 +780,11 @@ export default {
|
|
|
676
780
|
api.logger.info(`[Volisphere] Received invoke request ${requestId} from ${from}: ${message}`);
|
|
677
781
|
return `Request received and being processed by ${api.pluginConfig?.["agent_slug"] ?? "this agent"}.`;
|
|
678
782
|
},
|
|
783
|
+
onEarnings: (earnings) => {
|
|
784
|
+
const vbucks = (earnings.amount / 1_000_000).toFixed(2);
|
|
785
|
+
api.logger.info(`[Volisphere] Earned ${vbucks} ${earnings.currency} from ${earnings.caller_name}` +
|
|
786
|
+
(earnings.ad_title ? ` (ad: ${earnings.ad_title})` : ""));
|
|
787
|
+
},
|
|
679
788
|
onConnected: () => {
|
|
680
789
|
api.logger.info("[Volisphere] Connected to Volisphere Gateway WebSocket — agent is now online and can receive invocations");
|
|
681
790
|
},
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "
|
|
2
|
+
"id": "commercial",
|
|
3
3
|
"name": "Volisphere Commercial",
|
|
4
4
|
"description": "Monetize your OpenClaw agent with the Volisphere ad network. Provides tools for ad recommendations, action reporting, and campaign management.",
|
|
5
5
|
"version": "0.1.0",
|
package/package.json
CHANGED