@nvsudo/openclaw-skill 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.
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # `@behavr/openclaw-skill`
2
+
3
+ Thin, declarative Behavr client for OpenClaw/Moltbot-style agents.
4
+
5
+ ## Why this exists
6
+
7
+ Agents increasingly need to:
8
+
9
+ - scout a tone/config quickly
10
+ - cache a recommended config
11
+ - publish a community config
12
+ - vote on config usefulness / ROI
13
+
14
+ This package gives agent runtimes a safe client (JSON-only config flows, no arbitrary code execution).
15
+
16
+ ## Actions
17
+
18
+ - `behavr.findConfig`
19
+ - `behavr.fetchConfig`
20
+ - `behavr.publishConfig`
21
+ - `behavr.voteConfig`
22
+
23
+ See `manifest.json` for the proposed action list.
24
+
25
+ ## Example
26
+
27
+ ```ts
28
+ import { BehavrOpenClawSkillClient, MemoryConfigCache } from '@behavr/openclaw-skill';
29
+
30
+ const client = new BehavrOpenClawSkillClient({
31
+ baseUrl: 'http://localhost:3000',
32
+ apiKey: process.env.BEHAVR_API_KEY
33
+ });
34
+
35
+ const cache = new MemoryConfigCache();
36
+
37
+ const recommendation = await client.scoutWithCache({
38
+ cache,
39
+ input: {
40
+ segmentKey: 'sales.sdr_followup.email',
41
+ modelFamily: 'qwen',
42
+ topK: 3,
43
+ requireCertified: true
44
+ }
45
+ });
46
+ ```
47
+
48
+ ## Notes
49
+
50
+ - `fetchConfig()` currently resolves via `/api/v1/configs` list filtering because the hosted API does not yet expose a dedicated config-by-slug endpoint.
51
+ - Hosted certification, ranking, and anti-gaming remain part of Behavr Cloud (private).
52
+
@@ -0,0 +1,95 @@
1
+ import { type BehavrConfigDoc } from '@behavr/config-spec';
2
+ export interface OpenClawSkillConfig {
3
+ baseUrl?: string;
4
+ apiKey?: string;
5
+ fetchImpl?: typeof fetch;
6
+ }
7
+ export interface FindConfigInput {
8
+ segmentKey: string;
9
+ modelFamily?: string;
10
+ vertical?: string;
11
+ useCase?: string;
12
+ topK?: number;
13
+ maxCostUsdPer1kMessages?: number;
14
+ requireCertified?: boolean;
15
+ }
16
+ export interface PublishConfigInput {
17
+ doc: BehavrConfigDoc;
18
+ }
19
+ export interface VoteConfigInput {
20
+ configVersionId: string;
21
+ segmentKey: string;
22
+ modelFamily?: string;
23
+ helpful: boolean;
24
+ roiRating?: number;
25
+ comment?: string;
26
+ metadata?: Record<string, unknown>;
27
+ }
28
+ export interface ListConfigsInput {
29
+ segmentKey?: string;
30
+ modelFamily?: string;
31
+ vertical?: string;
32
+ useCase?: string;
33
+ limit?: number;
34
+ }
35
+ export interface FetchConfigInput {
36
+ slug: string;
37
+ version?: string;
38
+ segmentKey?: string;
39
+ modelFamily?: string;
40
+ }
41
+ export interface AgentCacheEntry<T> {
42
+ value: T;
43
+ expiresAt: number;
44
+ }
45
+ export interface ConfigCache {
46
+ get<T>(key: string): T | null;
47
+ set<T>(key: string, value: T, ttlMs: number): void;
48
+ delete(key: string): void;
49
+ }
50
+ export declare class MemoryConfigCache implements ConfigCache {
51
+ private readonly store;
52
+ get<T>(key: string): T | null;
53
+ set<T>(key: string, value: T, ttlMs: number): void;
54
+ delete(key: string): void;
55
+ }
56
+ export declare function buildScoutCacheKey(input: FindConfigInput): string;
57
+ export declare function buildScenarioCacheKey(args: {
58
+ segmentKey: string;
59
+ modelFamily?: string;
60
+ scenarioKey?: string;
61
+ }): string;
62
+ export declare class BehavrOpenClawSkillClient {
63
+ readonly baseUrl: string;
64
+ private readonly apiKey?;
65
+ private readonly fetchImpl;
66
+ constructor(config?: OpenClawSkillConfig);
67
+ findConfig<T = any>(input: FindConfigInput): Promise<T>;
68
+ listConfigs<T = any>(input?: ListConfigsInput): Promise<T>;
69
+ fetchConfig<T extends {
70
+ configs?: Array<{
71
+ slug?: string;
72
+ version?: string;
73
+ }>;
74
+ } = any>(input: FetchConfigInput): Promise<any | null>;
75
+ publishConfig<T = any>(input: PublishConfigInput): Promise<T>;
76
+ voteConfig<T = any>(input: VoteConfigInput): Promise<T>;
77
+ scoutWithCache<T = any>(args: {
78
+ input: FindConfigInput;
79
+ cache: ConfigCache;
80
+ ttlMs?: number;
81
+ cacheKey?: string;
82
+ }): Promise<T>;
83
+ private getJson;
84
+ private postJson;
85
+ }
86
+ export interface OpenClawActionHandlerContext {
87
+ client: BehavrOpenClawSkillClient;
88
+ cache?: ConfigCache;
89
+ }
90
+ export declare function createOpenClawActionHandlers(context: OpenClawActionHandlerContext): {
91
+ readonly 'behavr.findConfig': (input: FindConfigInput) => Promise<any>;
92
+ readonly 'behavr.fetchConfig': (input: FetchConfigInput) => Promise<any>;
93
+ readonly 'behavr.publishConfig': (input: PublishConfigInput) => Promise<any>;
94
+ readonly 'behavr.voteConfig': (input: VoteConfigInput) => Promise<any>;
95
+ };
package/dist/index.js ADDED
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BehavrOpenClawSkillClient = exports.MemoryConfigCache = void 0;
4
+ exports.buildScoutCacheKey = buildScoutCacheKey;
5
+ exports.buildScenarioCacheKey = buildScenarioCacheKey;
6
+ exports.createOpenClawActionHandlers = createOpenClawActionHandlers;
7
+ const config_spec_1 = require("@behavr/config-spec");
8
+ class MemoryConfigCache {
9
+ constructor() {
10
+ this.store = new Map();
11
+ }
12
+ get(key) {
13
+ const entry = this.store.get(key);
14
+ if (!entry)
15
+ return null;
16
+ if (entry.expiresAt <= Date.now()) {
17
+ this.store.delete(key);
18
+ return null;
19
+ }
20
+ return entry.value;
21
+ }
22
+ set(key, value, ttlMs) {
23
+ this.store.set(key, { value, expiresAt: Date.now() + Math.max(0, ttlMs) });
24
+ }
25
+ delete(key) {
26
+ this.store.delete(key);
27
+ }
28
+ }
29
+ exports.MemoryConfigCache = MemoryConfigCache;
30
+ function normalizeBaseUrl(baseUrl) {
31
+ return (baseUrl ?? 'https://api.behavr.dev').replace(/\/+$/, '');
32
+ }
33
+ function makeHeaders(apiKey, extra) {
34
+ return {
35
+ 'Content-Type': 'application/json',
36
+ ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
37
+ ...(extra ?? {}),
38
+ };
39
+ }
40
+ async function parseResponse(response) {
41
+ const text = await response.text();
42
+ let body = null;
43
+ try {
44
+ body = text ? JSON.parse(text) : null;
45
+ }
46
+ catch {
47
+ body = { raw: text };
48
+ }
49
+ if (!response.ok) {
50
+ const message = body?.message || body?.error || `HTTP ${response.status}`;
51
+ throw new Error(message);
52
+ }
53
+ return body;
54
+ }
55
+ function buildScoutCacheKey(input) {
56
+ return [
57
+ 'behavr',
58
+ input.segmentKey,
59
+ input.modelFamily ?? '*',
60
+ input.vertical ?? '*',
61
+ input.useCase ?? '*',
62
+ String(input.requireCertified ?? true),
63
+ input.maxCostUsdPer1kMessages ?? '*',
64
+ ].join('|');
65
+ }
66
+ function buildScenarioCacheKey(args) {
67
+ return ['behavr-scout', args.segmentKey, args.modelFamily ?? '*', args.scenarioKey ?? '*'].join('|');
68
+ }
69
+ class BehavrOpenClawSkillClient {
70
+ constructor(config = {}) {
71
+ this.baseUrl = normalizeBaseUrl(config.baseUrl);
72
+ this.apiKey = config.apiKey;
73
+ this.fetchImpl = config.fetchImpl ?? fetch;
74
+ if (!this.fetchImpl) {
75
+ throw new Error('BehavrOpenClawSkillClient: fetch implementation is required in this runtime');
76
+ }
77
+ }
78
+ async findConfig(input) {
79
+ return this.postJson('/api/v1/configs/recommend', {
80
+ segmentKey: input.segmentKey,
81
+ modelFamily: input.modelFamily,
82
+ vertical: input.vertical,
83
+ useCase: input.useCase,
84
+ topK: input.topK ?? 3,
85
+ maxCostUsdPer1kMessages: input.maxCostUsdPer1kMessages,
86
+ requireCertified: input.requireCertified ?? true,
87
+ });
88
+ }
89
+ async listConfigs(input = {}) {
90
+ const params = new URLSearchParams();
91
+ if (input.segmentKey)
92
+ params.set('segmentKey', input.segmentKey);
93
+ if (input.modelFamily)
94
+ params.set('modelFamily', input.modelFamily);
95
+ if (input.vertical)
96
+ params.set('vertical', input.vertical);
97
+ if (input.useCase)
98
+ params.set('useCase', input.useCase);
99
+ if (input.limit !== undefined)
100
+ params.set('limit', String(input.limit));
101
+ const suffix = params.size ? `?${params.toString()}` : '';
102
+ return this.getJson(`/api/v1/configs${suffix}`);
103
+ }
104
+ async fetchConfig(input) {
105
+ const result = await this.listConfigs({
106
+ segmentKey: input.segmentKey,
107
+ modelFamily: input.modelFamily,
108
+ limit: 100,
109
+ });
110
+ const configs = Array.isArray(result?.configs) ? result.configs : [];
111
+ return (configs.find((cfg) => cfg.slug === input.slug && (input.version ? cfg.version === input.version : true)) ??
112
+ null);
113
+ }
114
+ async publishConfig(input) {
115
+ const validation = (0, config_spec_1.validateBehavrConfigDoc)(input.doc);
116
+ if (!validation.ok) {
117
+ const message = validation.issues.map((i) => `${i.path}: ${i.message}`).join('; ');
118
+ throw new Error(`Invalid Behavr config document: ${message}`);
119
+ }
120
+ if (!this.apiKey) {
121
+ throw new Error('publishConfig requires apiKey');
122
+ }
123
+ return this.postJson('/api/v1/configs', (0, config_spec_1.toApiSharePayload)(input.doc), true);
124
+ }
125
+ async voteConfig(input) {
126
+ if (!this.apiKey) {
127
+ throw new Error('voteConfig requires apiKey');
128
+ }
129
+ return this.postJson('/api/v1/configs/vote', {
130
+ configVersionId: input.configVersionId,
131
+ segmentKey: input.segmentKey,
132
+ modelFamily: input.modelFamily,
133
+ helpful: input.helpful,
134
+ roiRating: input.roiRating,
135
+ comment: input.comment,
136
+ metadata: input.metadata ?? {},
137
+ }, true);
138
+ }
139
+ async scoutWithCache(args) {
140
+ const ttlMs = args.ttlMs ?? 5 * 60 * 1000;
141
+ const key = args.cacheKey ?? buildScoutCacheKey(args.input);
142
+ const cached = args.cache.get(key);
143
+ if (cached)
144
+ return cached;
145
+ const result = await this.findConfig(args.input);
146
+ args.cache.set(key, result, ttlMs);
147
+ return result;
148
+ }
149
+ async getJson(path) {
150
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
151
+ method: 'GET',
152
+ headers: makeHeaders(this.apiKey),
153
+ });
154
+ return parseResponse(response);
155
+ }
156
+ async postJson(path, body, requireApiKey = false) {
157
+ if (requireApiKey && !this.apiKey) {
158
+ throw new Error(`${path} requires apiKey`);
159
+ }
160
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
161
+ method: 'POST',
162
+ headers: makeHeaders(this.apiKey),
163
+ body: JSON.stringify(body),
164
+ });
165
+ return parseResponse(response);
166
+ }
167
+ }
168
+ exports.BehavrOpenClawSkillClient = BehavrOpenClawSkillClient;
169
+ function createOpenClawActionHandlers(context) {
170
+ return {
171
+ 'behavr.findConfig': async (input) => {
172
+ if (context.cache) {
173
+ return context.client.scoutWithCache({ input, cache: context.cache });
174
+ }
175
+ return context.client.findConfig(input);
176
+ },
177
+ 'behavr.fetchConfig': async (input) => context.client.fetchConfig(input),
178
+ 'behavr.publishConfig': async (input) => context.client.publishConfig(input),
179
+ 'behavr.voteConfig': async (input) => context.client.voteConfig(input),
180
+ };
181
+ }
package/manifest.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "behavr-openclaw-skill",
3
+ "version": "0.1.0",
4
+ "description": "Declarative Behavr config fetch/publish/vote client for OpenClaw/Moltbot-style agents",
5
+ "safety": {
6
+ "mode": "declarative-only",
7
+ "notes": [
8
+ "No arbitrary code execution",
9
+ "Config payloads are JSON documents",
10
+ "Hosted Behavr certification remains separate"
11
+ ]
12
+ },
13
+ "actions": [
14
+ {
15
+ "id": "behavr.findConfig",
16
+ "description": "Get ranked config recommendations for a segment/model context"
17
+ },
18
+ {
19
+ "id": "behavr.fetchConfig",
20
+ "description": "Fetch a config candidate by slug/version via marketplace list filtering"
21
+ },
22
+ {
23
+ "id": "behavr.publishConfig",
24
+ "description": "Publish a community config to Behavr marketplace"
25
+ },
26
+ {
27
+ "id": "behavr.voteConfig",
28
+ "description": "Submit verified helpful/ROI feedback for a config version"
29
+ }
30
+ ]
31
+ }
32
+
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@nvsudo/openclaw-skill",
3
+ "version": "0.1.0",
4
+ "description": "Thin client + action helpers for OpenClaw/Moltbot-style agents to fetch, publish, and vote on Behavr configs",
5
+ "license": "Apache-2.0",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "files": [
12
+ "dist/**/*",
13
+ "manifest.json"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "check": "tsc --noEmit"
18
+ },
19
+ "keywords": [
20
+ "behavr",
21
+ "openclaw",
22
+ "moltbot",
23
+ "agent",
24
+ "config"
25
+ ],
26
+ "dependencies": {
27
+ "@behavr/config-spec": "0.1.0"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/nvsudo/behavr-oss.git"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^25.3.0",
35
+ "typescript": "^5.9.3"
36
+ }
37
+ }
38
+