@t54-labs/clawcredit-sdk 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/SKILL.md ADDED
@@ -0,0 +1,106 @@
1
+ # ClawCredit Integration Skill
2
+
3
+ ## 1. Overview
4
+ This skill allows Open Claw Agents to access **ClawCredit**, a decentralized credit service.
5
+ By integrating the `@clawcredit/sdk`, agents can:
6
+ 1. **Get a Credit Line**: Prove your worth (code, prompt, environment) and get funded.
7
+ 2. **Pay for Services**: Use your credit to pay other agents/merchants via x402.
8
+ 3. **Build Credit**: Good repayment and low-risk behavior increases your limit.
9
+
10
+ ## 2. Prerequisites
11
+ - **Node.js Environment** (The SDK is a Node.js package).
12
+ - **Invitation Code**: You need a valid `invite_code` to register.
13
+
14
+ ## 3. Installation
15
+ Instruct the user (or run this in your shell if allowed) to install the SDK:
16
+
17
+ ```bash
18
+ npm install @t54-labs/clawcredit-sdk
19
+ ```
20
+
21
+ ## 4. Usage Guide
22
+
23
+ ### Step 1: Initialization & Registration (with Code Audit)
24
+ You must register to receive your initial Credit Line.
25
+ **IMPORTANT**: Use the `audit()` wrapper on your core logic functions. This proves to the Risk Engine that your code is safe.
26
+
27
+ ```javascript
28
+ const { ClawCredit, audit } = require('@t54-labs/clawcredit-sdk');
29
+
30
+ // 1. Audit your core functions (Optional but recommended for higher limit)
31
+ function myCoreLogic(input) {
32
+ // ... implementation ...
33
+ }
34
+ const safeLogic = audit(myCoreLogic);
35
+
36
+ // 2. Initialize Client
37
+ const credit = new ClawCredit({
38
+ agentName: "MyAgentName"
39
+ });
40
+
41
+ // 3. Register
42
+ await credit.register({
43
+ inviteCode: "YOUR_INVITE_CODE",
44
+ coreCode: "const agent = ...",
45
+ systemPrompt: "You are a helpful assistant...",
46
+ runtimeEnv: "node-v22",
47
+ model: "gpt-5.2" // Optional
48
+ });
49
+ ```
50
+
51
+ ### Step 2: Enable Reasoning Trace
52
+ To ensure your transactions are approved, you must allow ClawCredit to trace your reasoning process.
53
+ Wrap your OpenAI client with `wrapOpenAI`.
54
+
55
+ ```javascript
56
+ const OpenAI = require('openai');
57
+ const { wrapOpenAI, withTrace } = require('@t54-labs/clawcredit-sdk');
58
+
59
+ // Wrap the client
60
+ const openai = wrapOpenAI(new OpenAI({ apiKey: '...' }));
61
+
62
+ // Wrap your execution loop
63
+ await withTrace(async () => {
64
+ // Call LLM
65
+ const completion = await openai.chat.completions.create({
66
+ messages: [{ role: "user", content: "Buy me a coffee service" }],
67
+ model: "gpt-5.2",
68
+ });
69
+
70
+ // If the LLM decides to call a tool (pay), the SDK will automatically
71
+ // attach the 'messages' and 'completion' as proof of reasoning.
72
+
73
+ // ... logic to call credit.pay() ...
74
+ });
75
+ ```
76
+
77
+ ### Step 3: Making Payments (Settlement via x402)
78
+ When you need to pay a merchant (another Agent or Service), use `pay()`.
79
+ If you used `withTrace` and `wrapOpenAI`, the reasoning is attached automatically.
80
+
81
+ ```javascript
82
+ const transactionResult = await credit.pay({
83
+ transaction: {
84
+ recipient: "merchant_address_or_id",
85
+ amount: 5.00, // USD
86
+ chain: "BASE",
87
+ asset: "USDC"
88
+ },
89
+ request_body: {
90
+ service_name: "PremiumSearch",
91
+ params: { query: "latest crypto prices" }
92
+ },
93
+ // Optional. If provided, it increases the approval rate.
94
+ // context: { reasoning_process: "..." }
95
+ });
96
+
97
+ if (transactionResult.status === 'success') {
98
+ console.log("Payment successful! Result:", transactionResult.merchant_response);
99
+ }
100
+ ```
101
+
102
+ ## 5. Settlement & Chains
103
+ ClawCredit handles the blockchain complexity.
104
+ - Your credit is denominated in **USD**.
105
+ - Settlements occur on **Base (USDC)**, **Solana (USDC)**, or **XRPL (RLUSD)**.
106
+ - The merchant receives the crypto; you spend your Credit.
Binary file
package/index.js ADDED
@@ -0,0 +1,156 @@
1
+ const axios = require('axios');
2
+ const { v4: uuidv4 } = require('uuid');
3
+ const { audit, getAuditedSnapshot } = require('./lib/audit');
4
+ const { wrapOpenAI, withTrace, getTraceContext } = require('./lib/monitor');
5
+
6
+ class ClawCredit {
7
+ constructor(config = {}) {
8
+ this.serviceUrl = config.serviceUrl || 'https://api.clawcredit.com'; // Default production URL
9
+ this.agentName = config.agentName;
10
+ this.apiToken = config.apiToken; // Token stored after registration
11
+ this.agentId = config.agentId;
12
+ }
13
+
14
+ /**
15
+ * Register the agent to get a credit limit.
16
+ * Requires Invitation Code and Audit Materials.
17
+ */
18
+ async register({ inviteCode, coreCode, systemPrompt, runtimeEnv, model }) {
19
+ if (!inviteCode) throw new Error("Invitation Code is required.");
20
+
21
+ // Include audited functions if available (via audit decorator)
22
+ const auditedFunctions = getAuditedSnapshot();
23
+
24
+ try {
25
+ const payload = {
26
+ agent_name: this.agentName,
27
+ invite_code: inviteCode,
28
+ audit_material: {
29
+ core_code: coreCode, // Main entry code provided manually
30
+ audited_functions: auditedFunctions, // Auto-collected functions
31
+ system_prompt: systemPrompt,
32
+ runtime_env: runtimeEnv,
33
+ model: model || null
34
+ }
35
+ };
36
+
37
+ const res = await axios.post(`${this.serviceUrl}/v1/agent/register`, payload);
38
+
39
+ // Save credentials
40
+ this.agentId = res.data.agent_id;
41
+ this.apiToken = res.data.api_token;
42
+
43
+ return {
44
+ agent_id: this.agentId,
45
+ credit_limit: res.data.credit_limit,
46
+ message: "Registration successful. Credit line issued."
47
+ };
48
+ } catch (error) {
49
+ this._handleError(error);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Get current Balance and Credit Score.
55
+ */
56
+ async getBalance() {
57
+ this._ensureAuth();
58
+ try {
59
+ const res = await axios.get(`${this.serviceUrl}/v1/credit/balance`, {
60
+ headers: { 'Authorization': `Bearer ${this.apiToken}` }
61
+ });
62
+ return res.data;
63
+ // Returns: { available_usd: 100.00, credit_score: 650, pending_bills: 0 }
64
+ } catch (error) {
65
+ this._handleError(error);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Pay a merchant using ClawCredit.
71
+ * Settlements are handled via x402 on Base/Solana/XRPL.
72
+ */
73
+ async pay({ transaction, request_body, context }) {
74
+ this._ensureAuth();
75
+
76
+ // 1. Try to get Trace Context from AsyncLocalStorage
77
+ const autoTrace = getTraceContext();
78
+
79
+ // 2. Merge Manual Context with Auto Trace
80
+ let reasoning = null;
81
+ if (context && context.reasoning_process) {
82
+ reasoning = context.reasoning_process;
83
+ }
84
+
85
+ // We do NOT set 'reasoning' to raw messages because it expects a String.
86
+ // The backend will check 'raw_trace' if 'reasoning_process' is null.
87
+
88
+ const finalAuditContext = {
89
+ reasoning_process: reasoning, // Explicit reasoning string or null
90
+ raw_trace: autoTrace.last_llm_call || {}, // Full messages/tools/response
91
+ current_task: (context && context.current_task) || "unknown",
92
+ timestamp: Date.now()
93
+ };
94
+
95
+ // Validate: We need *some* proof of reasoning.
96
+ if (!finalAuditContext.reasoning_process && !finalAuditContext.raw_trace.messages) {
97
+ throw new Error("Payment rejected: No 'reasoning_process' provided and no active LLM trace found.");
98
+ }
99
+
100
+ if (!transaction || !transaction.recipient || !transaction.amount || !transaction.chain || !transaction.asset) {
101
+ throw new Error("Payment rejected: transaction must include recipient, amount, chain, and asset.");
102
+ }
103
+ if (!request_body) {
104
+ throw new Error("Payment rejected: request_body is required.");
105
+ }
106
+
107
+ const txRequest = {
108
+ transaction: {
109
+ recipient: transaction.recipient,
110
+ amount: transaction.amount,
111
+ chain: transaction.chain,
112
+ asset: transaction.asset
113
+ },
114
+ request_body: request_body,
115
+ audit_context: finalAuditContext
116
+ };
117
+
118
+ try {
119
+ // 3. Send Transaction Request to ClawCredit Service
120
+ const res = await axios.post(`${this.serviceUrl}/v1/transaction/pay`, txRequest, {
121
+ headers: { 'Authorization': `Bearer ${this.apiToken}` }
122
+ });
123
+
124
+ return {
125
+ status: 'success',
126
+ tx_hash: res.data.tx_hash, // Blockchain Tx Hash (Base/Solana/XRPL)
127
+ chain: res.data.chain, // e.g. "BASE"
128
+ merchant_response: res.data.merchant_response // The actual service result (e.g. search results)
129
+ };
130
+
131
+ } catch (error) {
132
+ this._handleError(error);
133
+ }
134
+ }
135
+
136
+ _ensureAuth() {
137
+ if (!this.apiToken) {
138
+ throw new Error("ClawCredit: Not authenticated. Please call register() first.");
139
+ }
140
+ }
141
+
142
+ _handleError(error) {
143
+ if (error.response) {
144
+ throw new Error(`ClawCredit API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
145
+ }
146
+ throw error;
147
+ }
148
+ }
149
+
150
+ // Export the Main Class and the Helper Functions
151
+ module.exports = {
152
+ ClawCredit,
153
+ audit,
154
+ wrapOpenAI,
155
+ withTrace
156
+ };
package/lib/audit.js ADDED
@@ -0,0 +1,53 @@
1
+ const crypto = require('crypto');
2
+ const { auditedEntities } = require('./store');
3
+
4
+ /**
5
+ * Normalizes code by removing extra whitespace to ensure consistent hashing.
6
+ */
7
+ function normalizeCode(code) {
8
+ return code.replace(/\s+/g, ' ').trim();
9
+ }
10
+
11
+ /**
12
+ * "Decorator" wrapper to audit a function.
13
+ * Since JS decorators aren't standard yet in all envs, we use a wrapper function.
14
+ * Usage: const safeFn = audit(originalFn);
15
+ */
16
+ function audit(fn, nameOverride = null) {
17
+ if (typeof fn !== 'function') {
18
+ throw new Error("ClawCredit Audit: Argument must be a function");
19
+ }
20
+
21
+ const name = nameOverride || fn.name || 'anonymous';
22
+ const source = fn.toString();
23
+ const hash = crypto.createHash('sha256').update(normalizeCode(source)).digest('hex');
24
+
25
+ // Register metadata
26
+ auditedEntities.push({
27
+ name: name,
28
+ hash: hash,
29
+ source: source,
30
+ timestamp: Date.now()
31
+ });
32
+
33
+ // Return the original function (pass-through)
34
+ // We could wrap it to track execution, but for "Code Audit", static capture is sufficient.
35
+ return fn;
36
+ }
37
+
38
+ /**
39
+ * Get all audited functions formatted for submission
40
+ */
41
+ function getAuditedSnapshot() {
42
+ return auditedEntities.map(e => ({
43
+ function_name: e.name,
44
+ function_code: e.source,
45
+ hash: e.hash
46
+ }));
47
+ }
48
+
49
+ module.exports = {
50
+ audit,
51
+ getAuditedSnapshot
52
+ };
53
+
package/lib/monitor.js ADDED
@@ -0,0 +1,87 @@
1
+ const { traceStorage } = require('./store');
2
+
3
+ /**
4
+ * Wraps an OpenAI client instance to capture prompts and tools.
5
+ * Mimics the Python SDK's monkey-patching approach but for Node.js object.
6
+ */
7
+ function wrapOpenAI(openaiInstance) {
8
+ if (!openaiInstance || !openaiInstance.chat || !openaiInstance.chat.completions) {
9
+ console.warn("ClawCredit: Invalid OpenAI instance provided. Skipping wrapper.");
10
+ return openaiInstance;
11
+ }
12
+
13
+ const originalCreate = openaiInstance.chat.completions.create.bind(openaiInstance.chat.completions);
14
+
15
+ // Overwrite the create method
16
+ openaiInstance.chat.completions.create = async function(params, options) {
17
+ // 1. Capture Context (Prompt & Tools)
18
+ const traceData = {
19
+ messages: params.messages || [],
20
+ tools: params.tools || [],
21
+ model: params.model,
22
+ timestamp: Date.now()
23
+ };
24
+
25
+ // Store in AsyncLocalStorage so it's accessible in the same async flow (e.g. during payment)
26
+ // We use 'enterWith' or run the promise inside 'run'.
27
+ // However, 'enterWith' is discouraged. Better to store it if we can wrapping the whole execution.
28
+ // But for "Context retrieval" later, we assume the Agent flow is:
29
+ // LLM -> Tool Call (Payment).
30
+ // If we are INSIDE the Tool Call, we want the LLM context that triggered it.
31
+ // Wait, the Python SDK stores it BEFORE the LLM call, but the tool is called BY the LLM response processing.
32
+ // So the flow is: User -> LLM -> Response(ToolCall) -> Agent executes Tool.
33
+
34
+ // In Python's contextvars, the context propagates if set properly.
35
+ // Here, we just want to save "what was the last LLM call made in this flow".
36
+
37
+ // Let's just update the store if we are already in a store context, OR create a new one?
38
+ // Actually, typically the Agent Framework loop runs in some scope.
39
+ // If we want to capture "Reasoning", we need the messages that LED to this tool call.
40
+
41
+ // Strategy:
42
+ // We save the inputs to a global-ish store keyed by something, or rely on the user
43
+ // to pass the trace. But the requirement is "Trace Agent's reasoning".
44
+
45
+ // Let's start by saving it to the store.
46
+ const store = traceStorage.getStore() || {};
47
+ store.last_llm_call = traceData;
48
+
49
+ // If we are not inside a run(), this store might be lost.
50
+ // For now, we assume the user might wrap their main loop with traceStorage.run().
51
+ // If not, we might need a fallback or side-channel.
52
+ // But let's stick to the "best effort" AsyncLocalStorage.
53
+
54
+ // 2. Execute Original
55
+ const response = await originalCreate(params, options);
56
+
57
+ // 3. Capture Output (Reasoning/CoT)
58
+ if (store.last_llm_call) {
59
+ store.last_llm_call.response = response;
60
+ // Extract reasoning if present (e.g. O1 model or explicit CoT field)
61
+ // or just the content.
62
+ }
63
+
64
+ return response;
65
+ };
66
+
67
+ return openaiInstance;
68
+ }
69
+
70
+ /**
71
+ * Run a function within a Trace Context.
72
+ * The Agent should wrap its "Step" or "Loop" with this.
73
+ */
74
+ function withTrace(fn) {
75
+ return traceStorage.run({}, fn);
76
+ }
77
+
78
+ function getTraceContext() {
79
+ return traceStorage.getStore() || {};
80
+ }
81
+
82
+ module.exports = {
83
+ wrapOpenAI,
84
+ withTrace,
85
+ getTraceContext
86
+ };
87
+
package/lib/store.js ADDED
@@ -0,0 +1,13 @@
1
+ const { AsyncLocalStorage } = require('async_hooks');
2
+
3
+ // Global storage for trace context (Request-scoped)
4
+ const traceStorage = new AsyncLocalStorage();
5
+
6
+ // Global registry for audited functions (Application-scoped)
7
+ const auditedEntities = [];
8
+
9
+ module.exports = {
10
+ traceStorage,
11
+ auditedEntities
12
+ };
13
+
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@t54-labs/clawcredit-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Integration SDK for Open Claw Agents to access ClawCredit",
5
+ "main": "index.js",
6
+ "dependencies": {
7
+ "axios": "^1.6.0",
8
+ "uuid": "^9.0.0"
9
+ }
10
+ }
11
+