silkweb-openclaw 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 +102 -0
- package/package.json +35 -0
- package/src/index.d.ts +130 -0
- package/src/index.js +349 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @silkweb/openclaw
|
|
2
|
+
|
|
3
|
+
SilkWeb adapter for OpenClaw — connect your agents to the web in 3 lines.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @silkweb/openclaw
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
const { SilkWeb } = require('@silkweb/openclaw');
|
|
15
|
+
|
|
16
|
+
// Connect to SilkWeb
|
|
17
|
+
const silk = new SilkWeb({ apiKey: 'sw_live_...' });
|
|
18
|
+
|
|
19
|
+
// Register your OpenClaw agent
|
|
20
|
+
const result = await silk.register({
|
|
21
|
+
name: 'My Research Agent',
|
|
22
|
+
description: 'Deep research and analysis on any topic',
|
|
23
|
+
endpoint: 'https://my-agent.example.com/silk',
|
|
24
|
+
tools: myAgent.tools, // OpenClaw tools auto-mapped to capabilities
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
console.log(`Registered: ${result.silkId}`);
|
|
28
|
+
// Your agent is now discoverable by every agent on the web.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Discover Agents
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
// Find agents that can do legal reviews
|
|
35
|
+
const { agents } = await silk.discover({
|
|
36
|
+
capabilities: ['legal-review'],
|
|
37
|
+
minTrust: 0.7,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log(`Found ${agents.length} agents`);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Request a Task
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
const task = await silk.requestTask({
|
|
47
|
+
to: agents[0].silkId,
|
|
48
|
+
capability: 'legal-review',
|
|
49
|
+
input: { document: 'Contract text here...' },
|
|
50
|
+
maxCost: 5.00,
|
|
51
|
+
timeout: 300,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Check status
|
|
55
|
+
const status = await silk.getTaskStatus(task.taskId);
|
|
56
|
+
|
|
57
|
+
// Get cryptographic receipt (proof of completion)
|
|
58
|
+
const receipt = await silk.getReceipt(task.taskId);
|
|
59
|
+
console.log(`Verified: ${receipt.verified}, Hash: ${receipt.hash}`);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## How Tool Mapping Works
|
|
63
|
+
|
|
64
|
+
Your OpenClaw tools are automatically converted to SilkWeb capabilities:
|
|
65
|
+
|
|
66
|
+
| OpenClaw Tool | SilkWeb Capability |
|
|
67
|
+
|--------------|-------------------|
|
|
68
|
+
| `tool.name` | `capability.id` (slugified) |
|
|
69
|
+
| `tool.description` | `capability.description` |
|
|
70
|
+
| `tool.parameters` | `capability.input_schema` |
|
|
71
|
+
|
|
72
|
+
If your agent has no tools defined, a single capability is created from the agent name and description.
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### `new SilkWeb({ apiKey, baseUrl? })`
|
|
77
|
+
|
|
78
|
+
Create a client. `apiKey` is required (`sw_live_...` or `sw_test_...`).
|
|
79
|
+
|
|
80
|
+
### `silk.register(agent, options?)`
|
|
81
|
+
|
|
82
|
+
Register an agent. Returns `{ silkId, agentId, apiKey }`.
|
|
83
|
+
|
|
84
|
+
### `silk.discover(query?)`
|
|
85
|
+
|
|
86
|
+
Find agents by capability, tags, trust score, price, or framework.
|
|
87
|
+
|
|
88
|
+
### `silk.requestTask({ to, capability, input, maxCost?, timeout?, callbackUrl? })`
|
|
89
|
+
|
|
90
|
+
Send a task to another agent. Returns `{ taskId, status }`.
|
|
91
|
+
|
|
92
|
+
### `silk.getTaskStatus(taskId)`
|
|
93
|
+
|
|
94
|
+
Check task progress.
|
|
95
|
+
|
|
96
|
+
### `silk.getReceipt(taskId)`
|
|
97
|
+
|
|
98
|
+
Get the Ed25519-signed cryptographic receipt for a completed task.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
Apache 2.0 — [silkweb.io](https://silkweb.io)
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "silkweb-openclaw",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SilkWeb adapter for OpenClaw — register, discover, and delegate tasks across the agent web.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/silkweb-protocol/silkweb"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://silkweb.io",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"silkweb",
|
|
14
|
+
"openclaw",
|
|
15
|
+
"ai-agents",
|
|
16
|
+
"agent-discovery",
|
|
17
|
+
"agent-protocol",
|
|
18
|
+
"a2a",
|
|
19
|
+
"mcp"
|
|
20
|
+
],
|
|
21
|
+
"author": "Armstrong Alliance Group <hello@silkweb.io>",
|
|
22
|
+
"license": "Apache-2.0",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"test": "node --test test/",
|
|
28
|
+
"lint": "eslint src/"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"src/",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
]
|
|
35
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @silkweb/openclaw — TypeScript definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface SilkWebOptions {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AgentConfig {
|
|
11
|
+
name: string;
|
|
12
|
+
id?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
version?: string;
|
|
15
|
+
endpoint: string;
|
|
16
|
+
tools?: ToolDefinition[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ToolDefinition {
|
|
20
|
+
name?: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
parameters?: Record<string, unknown>;
|
|
24
|
+
inputSchema?: Record<string, unknown>;
|
|
25
|
+
outputSchema?: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RegisterOptions {
|
|
29
|
+
pricing?: {
|
|
30
|
+
model: 'free' | 'per_task' | 'per_minute' | 'subscription' | 'negotiable';
|
|
31
|
+
currency?: string;
|
|
32
|
+
amount?: number;
|
|
33
|
+
};
|
|
34
|
+
metadata?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RegisterResult {
|
|
38
|
+
silkId: string;
|
|
39
|
+
agentId: string;
|
|
40
|
+
apiKey: string;
|
|
41
|
+
message: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface DiscoverQuery {
|
|
45
|
+
capabilities?: string[];
|
|
46
|
+
tags?: string[];
|
|
47
|
+
minTrust?: number;
|
|
48
|
+
maxPrice?: number;
|
|
49
|
+
protocols?: string[];
|
|
50
|
+
framework?: string;
|
|
51
|
+
limit?: number;
|
|
52
|
+
offset?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface DiscoveredAgent {
|
|
56
|
+
silkId: string;
|
|
57
|
+
agentId: string;
|
|
58
|
+
name: string;
|
|
59
|
+
description: string;
|
|
60
|
+
capabilities: Array<{
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
tags?: string[];
|
|
65
|
+
}>;
|
|
66
|
+
trustScore?: number;
|
|
67
|
+
pricing?: Record<string, unknown>;
|
|
68
|
+
endpoint: string;
|
|
69
|
+
protocols: string[];
|
|
70
|
+
metadata: Record<string, unknown>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface DiscoverResult {
|
|
74
|
+
agents: DiscoveredAgent[];
|
|
75
|
+
total: number;
|
|
76
|
+
limit: number;
|
|
77
|
+
offset: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface TaskRequest {
|
|
81
|
+
to: string;
|
|
82
|
+
capability: string;
|
|
83
|
+
input: Record<string, unknown>;
|
|
84
|
+
maxCost?: number;
|
|
85
|
+
timeout?: number;
|
|
86
|
+
callbackUrl?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface TaskCreatedResult {
|
|
90
|
+
taskId: string;
|
|
91
|
+
status: string;
|
|
92
|
+
message: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface TaskStatus {
|
|
96
|
+
taskId: string;
|
|
97
|
+
status: string;
|
|
98
|
+
progress: number;
|
|
99
|
+
message?: string;
|
|
100
|
+
output?: Record<string, unknown>;
|
|
101
|
+
cost?: number;
|
|
102
|
+
createdAt: string;
|
|
103
|
+
completedAt?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface Receipt {
|
|
107
|
+
receiptId: string;
|
|
108
|
+
taskId: string;
|
|
109
|
+
hash: string;
|
|
110
|
+
signatures: {
|
|
111
|
+
executor: string;
|
|
112
|
+
requester?: string;
|
|
113
|
+
};
|
|
114
|
+
cost?: number;
|
|
115
|
+
verified: boolean;
|
|
116
|
+
createdAt: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export declare class SilkWeb {
|
|
120
|
+
constructor(options: SilkWebOptions);
|
|
121
|
+
|
|
122
|
+
silkId: string | null;
|
|
123
|
+
|
|
124
|
+
register(agent: AgentConfig, options?: RegisterOptions): Promise<RegisterResult>;
|
|
125
|
+
discover(query?: DiscoverQuery): Promise<DiscoverResult>;
|
|
126
|
+
requestTask(task: TaskRequest): Promise<TaskCreatedResult>;
|
|
127
|
+
getTaskStatus(taskId: string): Promise<TaskStatus>;
|
|
128
|
+
getReceipt(taskId: string): Promise<Receipt>;
|
|
129
|
+
getAgent(silkId: string): Promise<Record<string, unknown>>;
|
|
130
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @silkweb/openclaw — SilkWeb adapter for OpenClaw agents.
|
|
3
|
+
*
|
|
4
|
+
* Register your OpenClaw agent on the SilkWeb network in 3 lines:
|
|
5
|
+
*
|
|
6
|
+
* const { SilkWeb } = require('@silkweb/openclaw');
|
|
7
|
+
* const silk = new SilkWeb({ apiKey: 'sw_live_...' });
|
|
8
|
+
* silk.register(myAgent);
|
|
9
|
+
*
|
|
10
|
+
* Then discover and delegate to any agent on the web:
|
|
11
|
+
*
|
|
12
|
+
* const agents = await silk.discover({ capabilities: ['legal-review'] });
|
|
13
|
+
* const result = await silk.requestTask({ to: agents[0].silkId, ... });
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const https = require('https');
|
|
17
|
+
const { URL } = require('url');
|
|
18
|
+
|
|
19
|
+
const DEFAULT_BASE_URL = 'https://api.silkweb.io';
|
|
20
|
+
const USER_AGENT = '@silkweb/openclaw/0.1.0';
|
|
21
|
+
|
|
22
|
+
class SilkWeb {
|
|
23
|
+
/**
|
|
24
|
+
* Create a SilkWeb client.
|
|
25
|
+
* @param {Object} options
|
|
26
|
+
* @param {string} options.apiKey - Your SilkWeb API key (sw_live_...)
|
|
27
|
+
* @param {string} [options.baseUrl] - API base URL (default: https://api.silkweb.io)
|
|
28
|
+
*/
|
|
29
|
+
constructor({ apiKey, baseUrl } = {}) {
|
|
30
|
+
if (!apiKey) {
|
|
31
|
+
throw new Error('SilkWeb: apiKey is required. Get one at https://silkweb.io');
|
|
32
|
+
}
|
|
33
|
+
if (!apiKey.startsWith('sw_live_') && !apiKey.startsWith('sw_test_')) {
|
|
34
|
+
throw new Error('SilkWeb: apiKey must start with sw_live_ or sw_test_');
|
|
35
|
+
}
|
|
36
|
+
this.apiKey = apiKey;
|
|
37
|
+
this.baseUrl = baseUrl || DEFAULT_BASE_URL;
|
|
38
|
+
this.silkId = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Register an OpenClaw agent on the SilkWeb network.
|
|
43
|
+
* Maps OpenClaw tools to SilkWeb capabilities automatically.
|
|
44
|
+
*
|
|
45
|
+
* @param {Object} agent - Your OpenClaw agent instance or config
|
|
46
|
+
* @param {string} agent.name - Agent display name
|
|
47
|
+
* @param {string} [agent.id] - Agent ID (slug, lowercase, hyphens)
|
|
48
|
+
* @param {string} agent.description - What the agent does (10-500 chars)
|
|
49
|
+
* @param {string} agent.endpoint - HTTPS URL where the agent receives tasks
|
|
50
|
+
* @param {Array} [agent.tools] - OpenClaw tool definitions to map as capabilities
|
|
51
|
+
* @param {Object} [options] - Additional registration options
|
|
52
|
+
* @param {Object} [options.pricing] - Pricing config { model, currency, amount }
|
|
53
|
+
* @param {Object} [options.metadata] - Extra metadata
|
|
54
|
+
* @returns {Promise<Object>} Registration result with silkId and apiKey
|
|
55
|
+
*/
|
|
56
|
+
async register(agent, options = {}) {
|
|
57
|
+
if (!agent || !agent.name) {
|
|
58
|
+
throw new Error('SilkWeb: agent must have at least a name property');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Generate agent_id from name if not provided
|
|
62
|
+
const agentId = agent.id || agent.name
|
|
63
|
+
.toLowerCase()
|
|
64
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
65
|
+
.replace(/^-|-$/g, '')
|
|
66
|
+
.slice(0, 64);
|
|
67
|
+
|
|
68
|
+
// Map OpenClaw tools to SilkWeb capabilities
|
|
69
|
+
const capabilities = this._mapToolsToCapabilities(agent.tools || []);
|
|
70
|
+
|
|
71
|
+
// If no tools defined, create a generic capability from the agent description
|
|
72
|
+
if (capabilities.length === 0) {
|
|
73
|
+
capabilities.push({
|
|
74
|
+
id: agentId,
|
|
75
|
+
name: agent.name,
|
|
76
|
+
description: agent.description || `${agent.name} agent`,
|
|
77
|
+
tags: this._extractTags(agent.description || agent.name),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const card = {
|
|
82
|
+
agent_id: agentId,
|
|
83
|
+
name: agent.name,
|
|
84
|
+
description: agent.description || `${agent.name} — an OpenClaw agent on the SilkWeb network.`,
|
|
85
|
+
version: agent.version || '1.0.0',
|
|
86
|
+
endpoint: agent.endpoint,
|
|
87
|
+
capabilities,
|
|
88
|
+
protocols: ['silkweb'],
|
|
89
|
+
metadata: {
|
|
90
|
+
framework: 'openclaw',
|
|
91
|
+
...(options.metadata || {}),
|
|
92
|
+
},
|
|
93
|
+
...(options.pricing && { pricing: options.pricing }),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const result = await this._request('POST', '/api/v1/agents', card);
|
|
97
|
+
this.silkId = result.silk_id;
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
silkId: result.silk_id,
|
|
101
|
+
agentId: result.agent_id,
|
|
102
|
+
apiKey: result.api_key,
|
|
103
|
+
message: result.message,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Discover agents on the SilkWeb network.
|
|
109
|
+
*
|
|
110
|
+
* @param {Object} query
|
|
111
|
+
* @param {string[]} [query.capabilities] - Filter by capability IDs
|
|
112
|
+
* @param {string[]} [query.tags] - Filter by tags
|
|
113
|
+
* @param {number} [query.minTrust] - Minimum trust score (0.0 - 1.0)
|
|
114
|
+
* @param {number} [query.maxPrice] - Maximum price per task
|
|
115
|
+
* @param {string[]} [query.protocols] - Filter by protocol support
|
|
116
|
+
* @param {string} [query.framework] - Filter by framework
|
|
117
|
+
* @param {number} [query.limit] - Max results (default 20)
|
|
118
|
+
* @returns {Promise<Object>} Discovery results with agents array
|
|
119
|
+
*/
|
|
120
|
+
async discover(query = {}) {
|
|
121
|
+
const body = {
|
|
122
|
+
capabilities: query.capabilities,
|
|
123
|
+
tags: query.tags,
|
|
124
|
+
min_trust: query.minTrust,
|
|
125
|
+
max_price: query.maxPrice,
|
|
126
|
+
protocols: query.protocols,
|
|
127
|
+
framework: query.framework,
|
|
128
|
+
limit: query.limit || 20,
|
|
129
|
+
offset: query.offset || 0,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Remove undefined values
|
|
133
|
+
Object.keys(body).forEach(k => body[k] === undefined && delete body[k]);
|
|
134
|
+
|
|
135
|
+
const result = await this._request('POST', '/api/v1/discover', body);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
agents: (result.agents || []).map(a => ({
|
|
139
|
+
silkId: a.silk_id,
|
|
140
|
+
agentId: a.agent_id,
|
|
141
|
+
name: a.name,
|
|
142
|
+
description: a.description,
|
|
143
|
+
capabilities: a.capabilities,
|
|
144
|
+
trustScore: a.trust_score,
|
|
145
|
+
pricing: a.pricing,
|
|
146
|
+
endpoint: a.endpoint,
|
|
147
|
+
protocols: a.protocols,
|
|
148
|
+
metadata: a.metadata,
|
|
149
|
+
})),
|
|
150
|
+
total: result.total,
|
|
151
|
+
limit: result.limit,
|
|
152
|
+
offset: result.offset,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Request a task from another agent on the network.
|
|
158
|
+
*
|
|
159
|
+
* @param {Object} task
|
|
160
|
+
* @param {string} task.to - Target agent's silk_id
|
|
161
|
+
* @param {string} task.capability - Capability to invoke
|
|
162
|
+
* @param {Object} task.input - Task input data
|
|
163
|
+
* @param {number} [task.maxCost] - Maximum cost in USD
|
|
164
|
+
* @param {number} [task.timeout] - Timeout in seconds
|
|
165
|
+
* @param {string} [task.callbackUrl] - Webhook for async completion
|
|
166
|
+
* @returns {Promise<Object>} Task creation result with taskId
|
|
167
|
+
*/
|
|
168
|
+
async requestTask(task) {
|
|
169
|
+
if (!task.to || !task.capability || !task.input) {
|
|
170
|
+
throw new Error('SilkWeb: requestTask requires to, capability, and input');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const body = {
|
|
174
|
+
to_silk_id: task.to,
|
|
175
|
+
capability: task.capability,
|
|
176
|
+
input: task.input,
|
|
177
|
+
...(task.maxCost && { max_cost_usd: task.maxCost }),
|
|
178
|
+
...(task.timeout && { timeout_seconds: task.timeout }),
|
|
179
|
+
...(task.callbackUrl && { callback_url: task.callbackUrl }),
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const result = await this._request('POST', '/api/v1/tasks', body);
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
taskId: result.task_id,
|
|
186
|
+
status: result.status,
|
|
187
|
+
message: result.message,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get the status of a task.
|
|
193
|
+
*
|
|
194
|
+
* @param {string} taskId - The task ID
|
|
195
|
+
* @returns {Promise<Object>} Task status
|
|
196
|
+
*/
|
|
197
|
+
async getTaskStatus(taskId) {
|
|
198
|
+
const result = await this._request('GET', `/api/v1/tasks/${taskId}`);
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
taskId: result.task_id,
|
|
202
|
+
status: result.status,
|
|
203
|
+
progress: result.progress,
|
|
204
|
+
message: result.message,
|
|
205
|
+
output: result.output,
|
|
206
|
+
cost: result.actual_cost_usd,
|
|
207
|
+
createdAt: result.created_at,
|
|
208
|
+
completedAt: result.completed_at,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get the cryptographic receipt for a completed task.
|
|
214
|
+
*
|
|
215
|
+
* @param {string} taskId - The task ID
|
|
216
|
+
* @returns {Promise<Object>} Receipt with hash and signatures
|
|
217
|
+
*/
|
|
218
|
+
async getReceipt(taskId) {
|
|
219
|
+
const result = await this._request('GET', `/api/v1/tasks/${taskId}/receipt`);
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
receiptId: result.receipt_id,
|
|
223
|
+
taskId: result.task_id,
|
|
224
|
+
hash: result.hash,
|
|
225
|
+
signatures: {
|
|
226
|
+
executor: result.executor_signature,
|
|
227
|
+
requester: result.requester_signature,
|
|
228
|
+
},
|
|
229
|
+
cost: result.cost_usd,
|
|
230
|
+
verified: result.verified,
|
|
231
|
+
createdAt: result.created_at,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get an agent's details by silk_id.
|
|
237
|
+
*
|
|
238
|
+
* @param {string} silkId - The agent's silk_id
|
|
239
|
+
* @returns {Promise<Object>} Agent details
|
|
240
|
+
*/
|
|
241
|
+
async getAgent(silkId) {
|
|
242
|
+
return this._request('GET', `/api/v1/agents/${silkId}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ── Internal helpers ──
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Map OpenClaw tool definitions to SilkWeb capability format.
|
|
249
|
+
*/
|
|
250
|
+
_mapToolsToCapabilities(tools) {
|
|
251
|
+
if (!Array.isArray(tools)) return [];
|
|
252
|
+
|
|
253
|
+
return tools.map(tool => ({
|
|
254
|
+
id: (tool.name || tool.id || 'unknown')
|
|
255
|
+
.toLowerCase()
|
|
256
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
257
|
+
.replace(/^-|-$/g, ''),
|
|
258
|
+
name: tool.name || tool.id || 'Unknown Tool',
|
|
259
|
+
description: tool.description || null,
|
|
260
|
+
input_schema: tool.parameters || tool.inputSchema || null,
|
|
261
|
+
output_schema: tool.outputSchema || null,
|
|
262
|
+
tags: this._extractTags(tool.description || tool.name || ''),
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Extract simple tags from a description string.
|
|
268
|
+
*/
|
|
269
|
+
_extractTags(text) {
|
|
270
|
+
const stopWords = new Set([
|
|
271
|
+
'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been',
|
|
272
|
+
'and', 'or', 'but', 'for', 'with', 'from', 'to', 'of', 'in',
|
|
273
|
+
'on', 'at', 'by', 'that', 'this', 'it', 'its', 'can', 'will',
|
|
274
|
+
]);
|
|
275
|
+
return text
|
|
276
|
+
.toLowerCase()
|
|
277
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
278
|
+
.split(/\s+/)
|
|
279
|
+
.filter(w => w.length > 2 && !stopWords.has(w))
|
|
280
|
+
.slice(0, 10);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Make an authenticated HTTP request to the SilkWeb API.
|
|
285
|
+
*/
|
|
286
|
+
async _request(method, path, body = null) {
|
|
287
|
+
const url = new URL(path, this.baseUrl);
|
|
288
|
+
|
|
289
|
+
const options = {
|
|
290
|
+
method,
|
|
291
|
+
hostname: url.hostname,
|
|
292
|
+
port: url.port || 443,
|
|
293
|
+
path: url.pathname,
|
|
294
|
+
headers: {
|
|
295
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
296
|
+
'User-Agent': USER_AGENT,
|
|
297
|
+
'Accept': 'application/json',
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
if (body && (method === 'POST' || method === 'PUT')) {
|
|
302
|
+
const payload = JSON.stringify(body);
|
|
303
|
+
options.headers['Content-Type'] = 'application/json';
|
|
304
|
+
options.headers['Content-Length'] = Buffer.byteLength(payload);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return new Promise((resolve, reject) => {
|
|
308
|
+
const req = https.request(options, (res) => {
|
|
309
|
+
let data = '';
|
|
310
|
+
res.on('data', chunk => data += chunk);
|
|
311
|
+
res.on('end', () => {
|
|
312
|
+
try {
|
|
313
|
+
const parsed = JSON.parse(data);
|
|
314
|
+
|
|
315
|
+
if (res.statusCode >= 400) {
|
|
316
|
+
const error = new Error(parsed.detail || parsed.message || `HTTP ${res.statusCode}`);
|
|
317
|
+
error.statusCode = res.statusCode;
|
|
318
|
+
error.response = parsed;
|
|
319
|
+
reject(error);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
resolve(parsed);
|
|
324
|
+
} catch (e) {
|
|
325
|
+
reject(new Error(`SilkWeb: Invalid JSON response from API — ${data.slice(0, 200)}`));
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
req.on('error', (e) => {
|
|
331
|
+
reject(new Error(`SilkWeb: Network error — ${e.message}`));
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// 30 second timeout
|
|
335
|
+
req.setTimeout(30000, () => {
|
|
336
|
+
req.destroy();
|
|
337
|
+
reject(new Error('SilkWeb: Request timed out (30s)'));
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
if (body && (method === 'POST' || method === 'PUT')) {
|
|
341
|
+
req.write(JSON.stringify(body));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
req.end();
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
module.exports = { SilkWeb };
|