@wzrd_sol/eliza-plugin 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/README.md +98 -26
- package/dist/actions/intel-preflight.d.ts +5 -0
- package/dist/actions/intel-preflight.js +97 -0
- package/dist/actions/intel-trust.d.ts +7 -0
- package/dist/actions/intel-trust.js +82 -0
- package/dist/actions/verify-receipt.d.ts +7 -0
- package/dist/actions/verify-receipt.js +44 -0
- package/dist/client-factory.d.ts +9 -0
- package/dist/client-factory.js +20 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.js +4 -1
- package/dist/index.d.ts +17 -6
- package/dist/index.js +20 -5
- package/dist/intel-helpers.d.ts +22 -0
- package/dist/intel-helpers.js +181 -0
- package/dist/paying-fetch.d.ts +9 -0
- package/dist/paying-fetch.js +27 -0
- package/dist/test/plugin-registration.intel.d.ts +1 -0
- package/dist/test/plugin-registration.intel.js +253 -0
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @wzrd_sol/eliza-plugin
|
|
2
2
|
|
|
3
|
-
ElizaOS plugin for
|
|
3
|
+
ElizaOS plugin for WZRD Agent Intel and the legacy earn loop on Solana.
|
|
4
|
+
|
|
5
|
+
**Before any x402 spend:** run `WZRD_INTEL_PREFLIGHT` (free ReadinessCard). Escalate to `WZRD_INTEL_TRUST` only when you need a signed V5 receipt. Verify offline with `WZRD_VERIFY_RECEIPT`.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -10,52 +12,122 @@ npm install @wzrd_sol/eliza-plugin
|
|
|
10
12
|
|
|
11
13
|
## Configuration
|
|
12
14
|
|
|
13
|
-
Set these in your ElizaOS runtime settings or environment:
|
|
14
|
-
|
|
15
15
|
| Variable | Required | Default | Description |
|
|
16
16
|
|----------|----------|---------|-------------|
|
|
17
|
-
| `
|
|
18
|
-
| `WZRD_API_URL` | No | `https://api.twzrd.xyz` |
|
|
19
|
-
| `
|
|
17
|
+
| `WZRD_INTEL_URL` | No | `https://intel.twzrd.xyz` | Agent Intel API (preflight, trust, verify) |
|
|
18
|
+
| `WZRD_API_URL` | No | `https://api.twzrd.xyz` | Legacy earn API (infer/report/claim) |
|
|
19
|
+
| `SOLANA_PRIVATE_KEY` | Earn lane only | — | JSON array of secret key bytes for agent Ed25519 auth |
|
|
20
|
+
|
|
21
|
+
### Wiring a paying fetch (intel paid actions)
|
|
22
|
+
|
|
23
|
+
The plugin does **not** embed a wallet. Paid `WZRD_INTEL_TRUST` needs an x402-capable `fetch` from the host:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { AgentRuntime } from '@elizaos/core';
|
|
27
|
+
import wzrdPlugin, { setPayingFetch } from '@wzrd_sol/eliza-plugin';
|
|
28
|
+
import { agentcashFetch } from 'your-x402-wrapper'; // agentcash, twzrd-x402-gate, etc.
|
|
29
|
+
|
|
30
|
+
setPayingFetch(agentcashFetch);
|
|
31
|
+
|
|
32
|
+
const agent = new AgentRuntime({
|
|
33
|
+
plugins: [wzrdPlugin],
|
|
34
|
+
settings: {
|
|
35
|
+
WZRD_INTEL_URL: 'https://intel.twzrd.xyz',
|
|
36
|
+
SOLANA_PRIVATE_KEY: process.env.SOLANA_PRIVATE_KEY,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Without a paying fetch, `WZRD_INTEL_TRUST` returns the HTTP 402 requirements and an `npx agentcash@latest fetch ...` one-liner.
|
|
20
42
|
|
|
21
43
|
## Usage
|
|
22
44
|
|
|
23
45
|
```typescript
|
|
24
|
-
import { wzrdPlugin } from
|
|
46
|
+
import { wzrdPlugin } from '@wzrd_sol/eliza-plugin';
|
|
25
47
|
|
|
26
|
-
// Register with your ElizaOS agent
|
|
27
48
|
const agent = new AgentRuntime({
|
|
28
49
|
plugins: [wzrdPlugin],
|
|
29
|
-
// ... other config
|
|
30
50
|
});
|
|
31
51
|
```
|
|
32
52
|
|
|
33
|
-
|
|
53
|
+
Programmatic SDK calls (same types as the plugin) are re-exported:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { intelPreflight, fetchIntelTrust, verifyReceipt, IntelPaymentRequiredError } from '@wzrd_sol/eliza-plugin';
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Intel actions (primary)
|
|
60
|
+
|
|
61
|
+
| Action | Auth/Pay | Description |
|
|
62
|
+
|--------|----------|-------------|
|
|
63
|
+
| `WZRD_INTEL_PREFLIGHT` | Free | ReadinessCard: decision, trust_score, can_spend, caveats, paid upsell |
|
|
64
|
+
| `WZRD_INTEL_TRUST` | x402 (~0.05 USDC) | Paid trust payload + V5 `twzrd_receipt` (needs `setPayingFetch`) |
|
|
65
|
+
| `WZRD_VERIFY_RECEIPT` | Free (offline) | Recompute leaf + Ed25519 verify; no network when pubkey is known |
|
|
66
|
+
|
|
67
|
+
### Example prompts
|
|
68
|
+
|
|
69
|
+
- "Preflight Jupiter Quote Preview before I pay 0.25 USDC to 6EF8rrect..."
|
|
70
|
+
- "Get the trust receipt for seller JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"
|
|
71
|
+
- "Verify this receipt: {leaf, preimage, signature...}"
|
|
72
|
+
- "Is it safe to call the paid intel endpoint on this seller?"
|
|
73
|
+
|
|
74
|
+
### Free preflight (no wallet)
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { intelPreflightAction } from '@wzrd_sol/eliza-plugin';
|
|
78
|
+
|
|
79
|
+
// Handler reads seller_wallet / price_usdc / agent_intent from message content
|
|
80
|
+
await intelPreflightAction.handler(runtime, {
|
|
81
|
+
content: {
|
|
82
|
+
seller_wallet: 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4',
|
|
83
|
+
price_usdc: 0.25,
|
|
84
|
+
agent_intent: 'quote preview',
|
|
85
|
+
},
|
|
86
|
+
}, ...);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Legacy earn actions
|
|
34
90
|
|
|
35
91
|
| Action | Auth | Description |
|
|
36
92
|
|--------|------|-------------|
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
93
|
+
| `WZRD_INFER` | Agent Ed25519 | Server-witnessed inference; returns `execution_id` |
|
|
94
|
+
| `WZRD_REPORT` | Agent Ed25519 | Report outcome with `execution_id` for verified rewards |
|
|
95
|
+
| `WZRD_EARN` | Agent Ed25519 | Full infer → report → rewards check in one action |
|
|
96
|
+
| `WZRD_CLAIM` | Agent Ed25519 | Gasless CCM claim via relay |
|
|
97
|
+
| `WZRD_REWARDS` | Agent Ed25519 | Pending and lifetime CCM balance |
|
|
42
98
|
|
|
43
|
-
### Example prompts
|
|
99
|
+
### Example prompts (earn)
|
|
44
100
|
|
|
45
|
-
- "
|
|
46
|
-
- "
|
|
47
|
-
- "Check my WZRD
|
|
48
|
-
- "
|
|
49
|
-
|
|
101
|
+
- "Run inference through WZRD: explain quicksort in Python"
|
|
102
|
+
- "Earn some CCM on WZRD"
|
|
103
|
+
- "Check my WZRD rewards"
|
|
104
|
+
- "Claim my CCM"
|
|
105
|
+
|
|
106
|
+
## Test
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
cd agents/eliza-plugin
|
|
110
|
+
npm ci
|
|
111
|
+
npm run build
|
|
112
|
+
npm test
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`npm test` loads the plugin into a real `@elizaos/core` `AgentRuntime`, runs live free preflight against `intel.twzrd.xyz`, offline receipt verify, and a mocked paid trust path.
|
|
116
|
+
|
|
117
|
+
Manual earn smoke (requires `SOLANA_PRIVATE_KEY`):
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npx tsx test/earn-e2e.ts
|
|
121
|
+
```
|
|
50
122
|
|
|
51
123
|
## Links
|
|
52
124
|
|
|
53
|
-
- [
|
|
125
|
+
- [Agent Intel API](https://intel.twzrd.xyz)
|
|
126
|
+
- [Legacy API](https://api.twzrd.xyz)
|
|
54
127
|
- [SDK](https://www.npmjs.com/package/@wzrd_sol/sdk)
|
|
55
|
-
- [
|
|
56
|
-
- [
|
|
57
|
-
- [Agent Discovery](https://twzrd.xyz/llms.txt)
|
|
128
|
+
- [twzrd-agent-intel (Python/MCP)](https://github.com/twzrd-sol/wzrd-final/tree/main/packages/twzrd-agent-intel)
|
|
129
|
+
- [Agent discovery](https://twzrd.xyz/llms.txt)
|
|
58
130
|
|
|
59
131
|
## License
|
|
60
132
|
|
|
61
|
-
MIT
|
|
133
|
+
MIT
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { intelPreflight } from '@wzrd_sol/sdk';
|
|
2
|
+
import { getIntelBase, parsePreflightInput, withTimeout } from '../intel-helpers.js';
|
|
3
|
+
function isValidBase58Wallet(s) {
|
|
4
|
+
if (!s)
|
|
5
|
+
return false;
|
|
6
|
+
return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(s);
|
|
7
|
+
}
|
|
8
|
+
function validatePreflight(input) {
|
|
9
|
+
if (input.seller_wallet && !isValidBase58Wallet(input.seller_wallet))
|
|
10
|
+
return 'seller_wallet must be 32-44 char base58';
|
|
11
|
+
if (input.price_usdc != null && (input.price_usdc < 0 || !Number.isFinite(input.price_usdc)))
|
|
12
|
+
return 'price_usdc must be non-negative finite number';
|
|
13
|
+
if (input.resource_name && typeof input.resource_name !== 'string')
|
|
14
|
+
return 'resource_name must be string';
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function formatCard(card, preflightId) {
|
|
18
|
+
const lines = [
|
|
19
|
+
`ReadinessCard v${card.version}`,
|
|
20
|
+
`Decision: ${card.decision}`,
|
|
21
|
+
`Trust score: ${card.trust_score ?? 'n/a'}`,
|
|
22
|
+
`Can spend: ${card.can_spend ? 'yes' : 'no'}`,
|
|
23
|
+
];
|
|
24
|
+
if (preflightId)
|
|
25
|
+
lines.push(`Preflight ID: ${preflightId}`);
|
|
26
|
+
if (card.resource_name)
|
|
27
|
+
lines.push(`Resource: ${card.resource_name}`);
|
|
28
|
+
if (card.seller_wallet)
|
|
29
|
+
lines.push(`Seller: ${card.seller_wallet}`);
|
|
30
|
+
if (card.price_usdc != null)
|
|
31
|
+
lines.push(`Price: ${card.price_usdc} USDC`);
|
|
32
|
+
if (card.caveats?.length)
|
|
33
|
+
lines.push(`Caveats: ${card.caveats.join('; ')}`);
|
|
34
|
+
if (card.next_fixes?.length)
|
|
35
|
+
lines.push(`Next fixes: ${card.next_fixes.join('; ')}`);
|
|
36
|
+
if (card.paid_deep_dive) {
|
|
37
|
+
lines.push(`Paid deep dive available (${card.paid_price_usdc ?? 0.05} USDC) — use WZRD_INTEL_TRUST`);
|
|
38
|
+
}
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
41
|
+
export const intelPreflightAction = {
|
|
42
|
+
name: 'WZRD_INTEL_PREFLIGHT',
|
|
43
|
+
similes: ['WZRD_PREFLIGHT', 'INTEL_PREFLIGHT', 'READINESS_CARD'],
|
|
44
|
+
description: 'Free pre-spend ReadinessCard for a seller/resource/price/intent. Returns decision (allow/warn/block), ' +
|
|
45
|
+
'trust_score, can_spend, caveats, paid_deep_dive upsell, and root_provenance when applicable. ' +
|
|
46
|
+
'Run this before any x402 payment.',
|
|
47
|
+
examples: [
|
|
48
|
+
[
|
|
49
|
+
{
|
|
50
|
+
name: '{{user1}}',
|
|
51
|
+
content: {
|
|
52
|
+
text: 'Preflight Jupiter Quote Preview before I pay 0.25 USDC to 6EF8rrect...',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: '{{agentName}}',
|
|
57
|
+
content: { text: 'ReadinessCard: decision ALLOW, trust_score 72, can_spend yes.' },
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
],
|
|
61
|
+
validate: async () => true,
|
|
62
|
+
handler: async (runtime, message, _state, _opt, callback) => {
|
|
63
|
+
const content = (message.content ?? {});
|
|
64
|
+
const input = parsePreflightInput(content);
|
|
65
|
+
if (!input.seller_wallet && !input.resource_name && !input.resource_url && !input.agent_intent) {
|
|
66
|
+
await callback?.({
|
|
67
|
+
text: 'Provide seller_wallet, resource_name, resource_url, or agent_intent for preflight. ' +
|
|
68
|
+
'Example: "Preflight seller JUP6Lkb... at 0.25 USDC"',
|
|
69
|
+
});
|
|
70
|
+
return { success: false, error: 'Missing preflight input' };
|
|
71
|
+
}
|
|
72
|
+
const vErr = validatePreflight(input);
|
|
73
|
+
if (vErr) {
|
|
74
|
+
await callback?.({ text: `Invalid preflight input: ${vErr}` });
|
|
75
|
+
return { success: false, error: vErr };
|
|
76
|
+
}
|
|
77
|
+
const apiBase = getIntelBase(runtime);
|
|
78
|
+
try {
|
|
79
|
+
const res = await withTimeout(() => intelPreflight(input, apiBase));
|
|
80
|
+
const card = res.readiness_card;
|
|
81
|
+
if (!card) {
|
|
82
|
+
const text = `Preflight OK (legacy). decision=${res.decision ?? 'unknown'}, score=${res.trust_score ?? 'n/a'}`;
|
|
83
|
+
await callback?.({ text });
|
|
84
|
+
return { success: true, data: res };
|
|
85
|
+
}
|
|
86
|
+
const preflightId = res.preflight_id;
|
|
87
|
+
const text = formatCard(card, preflightId);
|
|
88
|
+
await callback?.({ text });
|
|
89
|
+
return { success: true, data: { ...res, readiness_card: card } };
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
93
|
+
await callback?.({ text: `Preflight failed: ${msg}` });
|
|
94
|
+
return { success: false, error: msg };
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WZRD_INTEL_TRUST — Paid trust payload + signed receipt (V5/V6) via x402-capable
|
|
3
|
+
* fetch. Preflight-gated: the free ReadinessCard runs BEFORE the payment and a
|
|
4
|
+
* decision=block aborts before any spend (the protocol's preflight-before-pay rule).
|
|
5
|
+
*/
|
|
6
|
+
import type { Action } from '@elizaos/core';
|
|
7
|
+
export declare const intelTrustAction: Action;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { fetchIntelTrust, preSpendGate, IntelPaymentRequiredError } from '@wzrd_sol/sdk';
|
|
2
|
+
import { extractPubkey, formatPaymentRequired, getIntelBase, withTimeout } from '../intel-helpers.js';
|
|
3
|
+
import { resolvePayingFetch } from '../paying-fetch.js';
|
|
4
|
+
export const intelTrustAction = {
|
|
5
|
+
name: 'WZRD_INTEL_TRUST',
|
|
6
|
+
similes: ['WZRD_TRUST_RECEIPT', 'INTEL_TRUST', 'GET_TRUST_RECEIPT'],
|
|
7
|
+
description: 'Paid GET /v1/intel/trust/{pubkey} (~0.05 USDC). Returns trust score + signed twzrd_receipt (V5/V6). ' +
|
|
8
|
+
'Runs the free preflight first and aborts on decision=block before spending. ' +
|
|
9
|
+
'Requires an x402-capable fetchImpl (setPayingFetch or host service). ' +
|
|
10
|
+
'Surfaces payment requirements if no payer is configured.',
|
|
11
|
+
examples: [
|
|
12
|
+
[
|
|
13
|
+
{ name: '{{user1}}', content: { text: 'Get the trust receipt for seller JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4' } },
|
|
14
|
+
{ name: '{{agentName}}', content: { text: 'Trust receipt received. score=42, receipt leaf=0x...' } },
|
|
15
|
+
],
|
|
16
|
+
],
|
|
17
|
+
validate: async () => true,
|
|
18
|
+
handler: async (runtime, message, _state, _opt, callback) => {
|
|
19
|
+
const content = (message.content ?? {});
|
|
20
|
+
const pubkey = extractPubkey(content);
|
|
21
|
+
if (!pubkey) {
|
|
22
|
+
await callback?.({ text: 'Provide a seller pubkey (32-44 char base58) for trust lookup.' });
|
|
23
|
+
return { success: false, error: 'Missing pubkey' };
|
|
24
|
+
}
|
|
25
|
+
const apiBase = getIntelBase(runtime);
|
|
26
|
+
const baseFetchImpl = resolvePayingFetch(runtime);
|
|
27
|
+
// preflight-before-pay: run the FREE ReadinessCard on the counterparty and
|
|
28
|
+
// abort on decision=block BEFORE any payment is signed/sent. failOpen=false so
|
|
29
|
+
// the block-on-block guarantee holds even if the gate errors — a reference
|
|
30
|
+
// plugin must demonstrate the safe posture, not bypass it. The preflight call
|
|
31
|
+
// is free (the gate only ever reads), so this adds no spend.
|
|
32
|
+
try {
|
|
33
|
+
const gate = await preSpendGate({ seller_wallet: pubkey }, { apiBase, failOpen: false, fetchImpl: baseFetchImpl });
|
|
34
|
+
if (!gate.allow) {
|
|
35
|
+
await callback?.({
|
|
36
|
+
text: `Preflight blocked the trust purchase for ${pubkey}.\n` +
|
|
37
|
+
`Decision: ${gate.decision}${gate.trustScore != null ? `, trust_score=${gate.trustScore}` : ''}\n` +
|
|
38
|
+
`Reason: ${gate.reason}\n` +
|
|
39
|
+
`No payment was sent.`,
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
success: false,
|
|
43
|
+
error: 'preflight_block',
|
|
44
|
+
data: { decision: gate.decision, trustScore: gate.trustScore, reason: gate.reason },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (gateErr) {
|
|
49
|
+
// failOpen=false means a gate error should NOT silently pay. Surface it and stop.
|
|
50
|
+
const msg = gateErr instanceof Error ? gateErr.message : String(gateErr);
|
|
51
|
+
await callback?.({ text: `Preflight gate unavailable (${msg}); not spending. Try again shortly.` });
|
|
52
|
+
return { success: false, error: 'preflight_unavailable', data: { detail: msg } };
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const res = await withTimeout((signal) => {
|
|
56
|
+
const abortingFetch = ((input, init) => baseFetchImpl(input, { ...(init || {}), signal }));
|
|
57
|
+
return fetchIntelTrust(pubkey, { apiBase, fetchImpl: abortingFetch });
|
|
58
|
+
});
|
|
59
|
+
const receipt = res.twzrd_receipt;
|
|
60
|
+
const text = `Trust payload for ${pubkey}\n` +
|
|
61
|
+
`Score: ${res.trust?.score ?? 'n/a'}\n` +
|
|
62
|
+
`Paid: ${res.paid ? 'yes' : 'no'}\n` +
|
|
63
|
+
(res.tx ? `Settlement tx: ${res.tx}\n` : '') +
|
|
64
|
+
(receipt
|
|
65
|
+
? `Receipt v${receipt.version}, leaf: ${receipt.leaf}\n` +
|
|
66
|
+
`Use WZRD_VERIFY_RECEIPT to verify offline.`
|
|
67
|
+
: 'No twzrd_receipt in response.');
|
|
68
|
+
await callback?.({ text });
|
|
69
|
+
return { success: true, data: res };
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (err instanceof IntelPaymentRequiredError) {
|
|
73
|
+
const text = formatPaymentRequired(err, apiBase, pubkey);
|
|
74
|
+
await callback?.({ text });
|
|
75
|
+
return { success: false, error: 'payment_required', data: { paymentRequirements: err.paymentRequirements } };
|
|
76
|
+
}
|
|
77
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
78
|
+
await callback?.({ text: `Intel trust failed: ${msg}` });
|
|
79
|
+
return { success: false, error: msg };
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WZRD_VERIFY_RECEIPT — Offline leaf + Ed25519 verification (no network).
|
|
3
|
+
* Handles both V5 and V6 receipts; the SDK selects the leaf binding from the
|
|
4
|
+
* receipt's domain (V6 binds the reputation_* provenance fields into the leaf).
|
|
5
|
+
*/
|
|
6
|
+
import type { Action } from '@elizaos/core';
|
|
7
|
+
export declare const verifyReceiptAction: Action;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { verifyReceipt } from '@wzrd_sol/sdk';
|
|
2
|
+
import { getIntelBase, parseReceipt, withTimeout } from '../intel-helpers.js';
|
|
3
|
+
export const verifyReceiptAction = {
|
|
4
|
+
name: 'WZRD_VERIFY_RECEIPT',
|
|
5
|
+
similes: ['WZRD_VERIFY', 'VERIFY_TWZRD_RECEIPT', 'CHECK_RECEIPT'],
|
|
6
|
+
description: 'Offline verify a TwzrdReceipt (V5 or V6): recompute keccak leaf from preimage and check Ed25519 signature ' +
|
|
7
|
+
'against the published TWZRD key. No network required when trustedPubkey is known.',
|
|
8
|
+
examples: [
|
|
9
|
+
[
|
|
10
|
+
{ name: '{{user1}}', content: { text: 'Verify this receipt: {"version":"v5","leaf":"0x...","preimage":{...}}' } },
|
|
11
|
+
{ name: '{{agentName}}', content: { text: 'Receipt valid: leaf OK, signature OK.' } },
|
|
12
|
+
],
|
|
13
|
+
],
|
|
14
|
+
validate: async () => true,
|
|
15
|
+
handler: async (runtime, message, _state, _opt, callback) => {
|
|
16
|
+
const content = (message.content ?? {});
|
|
17
|
+
const receipt = parseReceipt(content);
|
|
18
|
+
if (!receipt) {
|
|
19
|
+
await callback?.({
|
|
20
|
+
text: 'Provide a TwzrdReceipt as JSON in text or as content.receipt.',
|
|
21
|
+
});
|
|
22
|
+
return { success: false, error: 'Missing receipt' };
|
|
23
|
+
}
|
|
24
|
+
const apiBase = getIntelBase(runtime);
|
|
25
|
+
const fetchPubkey = content.fetch_pubkey === true || content.fetchPubkey === true;
|
|
26
|
+
try {
|
|
27
|
+
const result = await withTimeout(() => verifyReceipt(receipt, { apiBase, fetchPubkey }));
|
|
28
|
+
const text = result.valid
|
|
29
|
+
? `Receipt VALID (leaf=${result.leafValid}, sig=${result.signatureValid}, key=${result.trustedPubkey})`
|
|
30
|
+
: `Receipt INVALID: ${result.errors.join('; ') || 'unknown error'}`;
|
|
31
|
+
await callback?.({ text });
|
|
32
|
+
return {
|
|
33
|
+
success: result.valid,
|
|
34
|
+
data: result,
|
|
35
|
+
error: result.valid ? undefined : result.errors.join('; '),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40
|
+
await callback?.({ text: `Verify failed: ${msg}` });
|
|
41
|
+
return { success: false, error: msg };
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
package/dist/client-factory.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { WzrdClient } from './client.js';
|
|
2
2
|
import type { IAgentRuntime } from '@elizaos/core';
|
|
3
|
+
import { intelPreflight, verifyReceipt } from '@wzrd_sol/sdk';
|
|
4
|
+
/** Intel API base URL from runtime settings (default https://intel.twzrd.xyz). */
|
|
5
|
+
export declare function getIntelApiBase(runtime: IAgentRuntime): string;
|
|
3
6
|
export declare function getWzrdClient(runtime: IAgentRuntime): WzrdClient;
|
|
4
7
|
/** Reset client cache — useful for tests. */
|
|
5
8
|
export declare function clearClientCache(): void;
|
|
9
|
+
/** Intel client (per plan): reads WZRD_INTEL_URL + paying fetch; thin delegate to SDK. */
|
|
10
|
+
export declare function getIntelClient(runtime: IAgentRuntime): {
|
|
11
|
+
preflight: (input: Parameters<typeof intelPreflight>[0]) => Promise<import("@wzrd_sol/sdk").PreflightResponse>;
|
|
12
|
+
trust: (pubkey: string) => Promise<import("@wzrd_sol/sdk").IntelTrustResponse>;
|
|
13
|
+
verify: (receipt: Parameters<typeof verifyReceipt>[0], opts?: Parameters<typeof verifyReceipt>[1]) => Promise<import("@wzrd_sol/sdk").VerifyReceiptResult>;
|
|
14
|
+
};
|
package/dist/client-factory.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
/** Extract keypair from ElizaOS runtime, return cached WzrdClient. */
|
|
2
2
|
import { Keypair } from '@solana/web3.js';
|
|
3
3
|
import { WzrdClient } from './client.js';
|
|
4
|
+
import { getIntelBase, withTimeout } from './intel-helpers.js';
|
|
5
|
+
import { resolvePayingFetch } from './paying-fetch.js';
|
|
6
|
+
import { intelPreflight, fetchIntelTrust, verifyReceipt } from '@wzrd_sol/sdk';
|
|
4
7
|
const cache = new Map();
|
|
8
|
+
/** Intel API base URL from runtime settings (default https://intel.twzrd.xyz). */
|
|
9
|
+
export function getIntelApiBase(runtime) {
|
|
10
|
+
return getIntelBase(runtime);
|
|
11
|
+
}
|
|
5
12
|
export function getWzrdClient(runtime) {
|
|
6
13
|
const sk = runtime.getSetting('SOLANA_PRIVATE_KEY');
|
|
7
14
|
if (!sk)
|
|
@@ -18,3 +25,16 @@ export function getWzrdClient(runtime) {
|
|
|
18
25
|
export function clearClientCache() {
|
|
19
26
|
cache.clear();
|
|
20
27
|
}
|
|
28
|
+
/** Intel client (per plan): reads WZRD_INTEL_URL + paying fetch; thin delegate to SDK. */
|
|
29
|
+
export function getIntelClient(runtime) {
|
|
30
|
+
const apiBase = getIntelApiBase(runtime);
|
|
31
|
+
return {
|
|
32
|
+
preflight: (input) => withTimeout(() => intelPreflight(input, apiBase)),
|
|
33
|
+
trust: (pubkey) => withTimeout((signal) => {
|
|
34
|
+
const f = resolvePayingFetch(runtime);
|
|
35
|
+
const abortingFetch = ((input, init) => f(input, { ...(init || {}), signal }));
|
|
36
|
+
return fetchIntelTrust(pubkey, { apiBase, fetchImpl: abortingFetch });
|
|
37
|
+
}),
|
|
38
|
+
verify: (receipt, opts) => withTimeout(() => verifyReceipt(receipt, { apiBase, ...(opts || {}) })),
|
|
39
|
+
};
|
|
40
|
+
}
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -18,7 +18,7 @@ export class WzrdClient {
|
|
|
18
18
|
if (this.token && Date.now() < this.tokenExpiry)
|
|
19
19
|
return this.token;
|
|
20
20
|
// 1. Get challenge
|
|
21
|
-
const challengeRes = await fetch(`${this.apiUrl}/v1/agent/challenge
|
|
21
|
+
const challengeRes = await fetch(`${this.apiUrl}/v1/agent/challenge`);
|
|
22
22
|
if (!challengeRes.ok)
|
|
23
23
|
throw new Error(`Challenge failed: ${challengeRes.status}`);
|
|
24
24
|
const { nonce } = await challengeRes.json();
|
|
@@ -132,3 +132,6 @@ export class WzrdClient {
|
|
|
132
132
|
return { ...data, claimable: data.cumulative_total - data.claimed_total };
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
+
// Thin wrappers delegating to @wzrd_sol/sdk intel surface (preflight/trust/verify) per plan.
|
|
136
|
+
// Kept in client.ts alongside earn WzrdClient. Actual runtime client for intel is via getIntelClient() in factory.
|
|
137
|
+
export { intelPreflight as intelPreflightClient, fetchIntelTrust as fetchIntelTrustClient, verifyReceipt as verifyReceiptClient, } from '@wzrd_sol/sdk';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @wzrd_sol/eliza-plugin — WZRD Earn Loop for ElizaOS
|
|
2
|
+
* @wzrd_sol/eliza-plugin — WZRD Agent Intel + Earn Loop for ElizaOS
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Intel lane (default https://intel.twzrd.xyz):
|
|
5
|
+
* WZRD_INTEL_PREFLIGHT → WZRD_INTEL_TRUST → WZRD_VERIFY_RECEIPT
|
|
6
|
+
* Paid intel requires caller-supplied x402 fetch via setPayingFetch().
|
|
7
|
+
*
|
|
8
|
+
* Earn lane (default https://api.twzrd.xyz):
|
|
9
|
+
* WZRD_INFER → WZRD_REPORT → WZRD_EARN → WZRD_CLAIM / WZRD_REWARDS
|
|
6
10
|
*
|
|
7
11
|
* Config (runtime.getSetting):
|
|
8
|
-
* SOLANA_PRIVATE_KEY — required
|
|
12
|
+
* SOLANA_PRIVATE_KEY — required for earn tx/auth
|
|
9
13
|
* WZRD_API_URL — optional, defaults to https://api.twzrd.xyz
|
|
14
|
+
* WZRD_INTEL_URL — optional, defaults to https://intel.twzrd.xyz
|
|
10
15
|
*/
|
|
11
16
|
import type { Plugin } from '@elizaos/core';
|
|
12
17
|
import { inferAction } from './actions/infer.js';
|
|
@@ -14,9 +19,15 @@ import { reportAction } from './actions/report.js';
|
|
|
14
19
|
import { earnAction } from './actions/earn.js';
|
|
15
20
|
import { claimAction } from './actions/claim.js';
|
|
16
21
|
import { rewardsAction } from './actions/rewards.js';
|
|
22
|
+
import { intelPreflightAction } from './actions/intel-preflight.js';
|
|
23
|
+
import { intelTrustAction } from './actions/intel-trust.js';
|
|
24
|
+
import { verifyReceiptAction } from './actions/verify-receipt.js';
|
|
17
25
|
export declare const wzrdPlugin: Plugin;
|
|
18
26
|
export default wzrdPlugin;
|
|
19
|
-
export { earnAction, inferAction, reportAction, claimAction, rewardsAction };
|
|
20
|
-
export { getWzrdClient, clearClientCache } from './client-factory.js';
|
|
27
|
+
export { intelPreflightAction, intelTrustAction, verifyReceiptAction, earnAction, inferAction, reportAction, claimAction, rewardsAction, };
|
|
28
|
+
export { getWzrdClient, clearClientCache, getIntelApiBase, getIntelClient } from './client-factory.js';
|
|
29
|
+
export { setPayingFetch, clearPayingFetch, resolvePayingFetch } from './paying-fetch.js';
|
|
21
30
|
export { WzrdClient } from './client.js';
|
|
22
31
|
export type { InferResult, ReportResult, RewardsBalance, ClaimResult } from './client.js';
|
|
32
|
+
export { IntelPaymentRequiredError, intelPreflight, fetchIntelTrust, verifyReceipt, preSpendGate, intelTrustUrl, TRUSTED_RECEIPT_PUBKEY, INTEL_TRUST_PRICE_USDC, } from '@wzrd_sol/sdk';
|
|
33
|
+
export type { ReadinessCard, PreflightInput, PreflightResponse, TwzrdReceipt, IntelTrustResponse, VerifyReceiptResult, X402PaymentRequired, } from '@wzrd_sol/sdk';
|
package/dist/index.js
CHANGED
|
@@ -3,13 +3,28 @@ import { reportAction } from './actions/report.js';
|
|
|
3
3
|
import { earnAction } from './actions/earn.js';
|
|
4
4
|
import { claimAction } from './actions/claim.js';
|
|
5
5
|
import { rewardsAction } from './actions/rewards.js';
|
|
6
|
+
import { intelPreflightAction } from './actions/intel-preflight.js';
|
|
7
|
+
import { intelTrustAction } from './actions/intel-trust.js';
|
|
8
|
+
import { verifyReceiptAction } from './actions/verify-receipt.js';
|
|
6
9
|
export const wzrdPlugin = {
|
|
7
10
|
name: 'wzrd',
|
|
8
|
-
description: 'WZRD
|
|
9
|
-
'
|
|
10
|
-
|
|
11
|
+
description: 'WZRD Agent Intel — free ReadinessCard preflight (gates spends before paying), x402-paid trust receipts ' +
|
|
12
|
+
'(V5/V6), offline verification on intel.twzrd.xyz. Also ships the legacy earn loop (infer/report/claim) ' +
|
|
13
|
+
'on api.twzrd.xyz.',
|
|
14
|
+
actions: [
|
|
15
|
+
intelPreflightAction,
|
|
16
|
+
intelTrustAction,
|
|
17
|
+
verifyReceiptAction,
|
|
18
|
+
earnAction,
|
|
19
|
+
inferAction,
|
|
20
|
+
reportAction,
|
|
21
|
+
claimAction,
|
|
22
|
+
rewardsAction,
|
|
23
|
+
],
|
|
11
24
|
};
|
|
12
25
|
export default wzrdPlugin;
|
|
13
|
-
export { earnAction, inferAction, reportAction, claimAction, rewardsAction };
|
|
14
|
-
export { getWzrdClient, clearClientCache } from './client-factory.js';
|
|
26
|
+
export { intelPreflightAction, intelTrustAction, verifyReceiptAction, earnAction, inferAction, reportAction, claimAction, rewardsAction, };
|
|
27
|
+
export { getWzrdClient, clearClientCache, getIntelApiBase, getIntelClient } from './client-factory.js';
|
|
28
|
+
export { setPayingFetch, clearPayingFetch, resolvePayingFetch } from './paying-fetch.js';
|
|
15
29
|
export { WzrdClient } from './client.js';
|
|
30
|
+
export { IntelPaymentRequiredError, intelPreflight, fetchIntelTrust, verifyReceipt, preSpendGate, intelTrustUrl, TRUSTED_RECEIPT_PUBKEY, INTEL_TRUST_PRICE_USDC, } from '@wzrd_sol/sdk';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { IntelPaymentRequiredError, type PreflightInput, type TwzrdReceipt } from '@wzrd_sol/sdk';
|
|
2
|
+
export declare function getIntelBase(runtime: {
|
|
3
|
+
getSetting: (k: string) => string | boolean | number | null;
|
|
4
|
+
}): string;
|
|
5
|
+
/** Pull structured fields from Eliza message content (flat or JSON-in-text). */
|
|
6
|
+
export declare function parsePreflightInput(content: Record<string, unknown>): PreflightInput;
|
|
7
|
+
export declare function parseReceipt(content: Record<string, unknown>): TwzrdReceipt | null;
|
|
8
|
+
export declare function extractPubkey(content: Record<string, unknown>): string | null;
|
|
9
|
+
export declare function formatPaymentRequired(err: IntelPaymentRequiredError, apiBase: string, pubkey: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Timeout wrapper for SDK network calls (preflight/trust/verify).
|
|
12
|
+
* Supports two call forms for minimal change:
|
|
13
|
+
* - withTimeout(promise) for preflight (SDK does not expose fetchImpl/signal)
|
|
14
|
+
* - withTimeout((signal) => sdkCallWithFetchImpl(abortingFetch)) for trust/verify
|
|
15
|
+
* Uses AbortController + signal: abort() is called on timeout so that when caller
|
|
16
|
+
* wires the returned signal into fetchImpl, the underlying network request is aborted.
|
|
17
|
+
* Always clears timer in finally (no leak). Attaches rejection observer to the
|
|
18
|
+
* call promise (without altering settlement) to avoid unhandled rejections on the
|
|
19
|
+
* loser of the race.
|
|
20
|
+
* Applied to actions + getIntelClient delegates.
|
|
21
|
+
*/
|
|
22
|
+
export declare function withTimeout<T>(pOrMake: Promise<T> | ((signal?: AbortSignal) => Promise<T>), ms?: number): Promise<T>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { intelTrustUrl, } from '@wzrd_sol/sdk';
|
|
2
|
+
const DEFAULT_INTEL = 'https://intel.twzrd.xyz';
|
|
3
|
+
export function getIntelBase(runtime) {
|
|
4
|
+
const url = runtime.getSetting('WZRD_INTEL_URL');
|
|
5
|
+
return typeof url === 'string' && url.length > 0 ? url : DEFAULT_INTEL;
|
|
6
|
+
}
|
|
7
|
+
/** Pull structured fields from Eliza message content (flat or JSON-in-text). */
|
|
8
|
+
export function parsePreflightInput(content) {
|
|
9
|
+
const direct = {
|
|
10
|
+
resource_name: str(content.resource_name),
|
|
11
|
+
seller_wallet: str(content.seller_wallet ?? content.pubkey ?? content.wallet),
|
|
12
|
+
resource_url: str(content.resource_url ?? content.url),
|
|
13
|
+
price_usdc: num(content.price_usdc ?? content.price),
|
|
14
|
+
buyer_wallet: str(content.buyer_wallet),
|
|
15
|
+
agent_intent: str(content.agent_intent ?? content.intent),
|
|
16
|
+
marketplace_score: num(content.marketplace_score),
|
|
17
|
+
};
|
|
18
|
+
const text = str(content.text);
|
|
19
|
+
if (text) {
|
|
20
|
+
const fromJson = tryParseJson(text);
|
|
21
|
+
if (fromJson)
|
|
22
|
+
return { ...direct, ...pickPreflight(fromJson) };
|
|
23
|
+
const wallet = extractWallet(text);
|
|
24
|
+
if (wallet && !direct.seller_wallet)
|
|
25
|
+
direct.seller_wallet = wallet;
|
|
26
|
+
if (!direct.agent_intent)
|
|
27
|
+
direct.agent_intent = text;
|
|
28
|
+
}
|
|
29
|
+
return compactPreflight(direct);
|
|
30
|
+
}
|
|
31
|
+
export function parseReceipt(content) {
|
|
32
|
+
if (content.receipt && typeof content.receipt === 'object') {
|
|
33
|
+
return content.receipt;
|
|
34
|
+
}
|
|
35
|
+
const text = str(content.text);
|
|
36
|
+
if (!text)
|
|
37
|
+
return null;
|
|
38
|
+
const parsed = tryParseJson(text);
|
|
39
|
+
if (parsed && typeof parsed === 'object' && 'leaf' in parsed && 'preimage' in parsed) {
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
export function extractPubkey(content) {
|
|
45
|
+
const direct = str(content.pubkey ?? content.seller_wallet ?? content.wallet);
|
|
46
|
+
if (direct)
|
|
47
|
+
return direct;
|
|
48
|
+
const text = str(content.text);
|
|
49
|
+
if (!text)
|
|
50
|
+
return null;
|
|
51
|
+
const fromJson = tryParseJson(text);
|
|
52
|
+
if (fromJson) {
|
|
53
|
+
const w = str(fromJson.pubkey ?? fromJson.seller_wallet ?? fromJson.wallet);
|
|
54
|
+
if (w)
|
|
55
|
+
return w;
|
|
56
|
+
}
|
|
57
|
+
return extractWallet(text);
|
|
58
|
+
}
|
|
59
|
+
export function formatPaymentRequired(err, apiBase, pubkey) {
|
|
60
|
+
const url = intelTrustUrl(pubkey, apiBase);
|
|
61
|
+
const reqs = err.paymentRequirements;
|
|
62
|
+
const accepts = reqs && typeof reqs === 'object' && 'accepts' in reqs && Array.isArray(reqs.accepts)
|
|
63
|
+
? reqs.accepts[0]
|
|
64
|
+
: null;
|
|
65
|
+
const amount = accepts?.amount ?? '50000';
|
|
66
|
+
const payTo = accepts?.payTo ?? '(see accepts)';
|
|
67
|
+
const network = accepts?.network ?? 'solana';
|
|
68
|
+
return (`Intel trust requires x402 payment (~0.05 USDC).\n` +
|
|
69
|
+
`Endpoint: ${url}\n` +
|
|
70
|
+
`Network: ${network}, amount (atomic): ${amount}, payTo: ${payTo}\n\n` +
|
|
71
|
+
`No paying fetch configured. Wire one before runtime creation:\n` +
|
|
72
|
+
` import { setPayingFetch } from '@wzrd_sol/eliza-plugin';\n` +
|
|
73
|
+
` setPayingFetch(myAgentcashFetch);\n\n` +
|
|
74
|
+
`Or pay out of band:\n` +
|
|
75
|
+
` npx agentcash@latest fetch ${url}`);
|
|
76
|
+
}
|
|
77
|
+
function str(v) {
|
|
78
|
+
if (typeof v === 'string' && v.trim())
|
|
79
|
+
return v.trim();
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
function num(v) {
|
|
83
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
84
|
+
return v;
|
|
85
|
+
if (typeof v === 'string' && v.trim()) {
|
|
86
|
+
const n = Number(v);
|
|
87
|
+
if (Number.isFinite(n))
|
|
88
|
+
return n;
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
function tryParseJson(text) {
|
|
93
|
+
const trimmed = text.trim();
|
|
94
|
+
if (!trimmed.startsWith('{'))
|
|
95
|
+
return null;
|
|
96
|
+
try {
|
|
97
|
+
const v = JSON.parse(trimmed);
|
|
98
|
+
return v && typeof v === 'object' ? v : null;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function extractWallet(text) {
|
|
105
|
+
const m = text.match(/[1-9A-HJ-NP-Za-km-z]{32,44}/);
|
|
106
|
+
return m ? m[0] : null;
|
|
107
|
+
}
|
|
108
|
+
function pickPreflight(obj) {
|
|
109
|
+
return compactPreflight({
|
|
110
|
+
resource_name: str(obj.resource_name),
|
|
111
|
+
seller_wallet: str(obj.seller_wallet ?? obj.pubkey ?? obj.wallet),
|
|
112
|
+
resource_url: str(obj.resource_url ?? obj.url),
|
|
113
|
+
price_usdc: num(obj.price_usdc ?? obj.price),
|
|
114
|
+
buyer_wallet: str(obj.buyer_wallet),
|
|
115
|
+
agent_intent: str(obj.agent_intent ?? obj.intent),
|
|
116
|
+
marketplace_score: num(obj.marketplace_score),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function compactPreflight(input) {
|
|
120
|
+
const out = {};
|
|
121
|
+
if (input.resource_name)
|
|
122
|
+
out.resource_name = input.resource_name;
|
|
123
|
+
if (input.seller_wallet)
|
|
124
|
+
out.seller_wallet = input.seller_wallet;
|
|
125
|
+
if (input.resource_url)
|
|
126
|
+
out.resource_url = input.resource_url;
|
|
127
|
+
if (input.price_usdc !== undefined)
|
|
128
|
+
out.price_usdc = input.price_usdc;
|
|
129
|
+
if (input.buyer_wallet)
|
|
130
|
+
out.buyer_wallet = input.buyer_wallet;
|
|
131
|
+
if (input.agent_intent)
|
|
132
|
+
out.agent_intent = input.agent_intent;
|
|
133
|
+
if (input.marketplace_score !== undefined)
|
|
134
|
+
out.marketplace_score = input.marketplace_score;
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Timeout wrapper for SDK network calls (preflight/trust/verify).
|
|
139
|
+
* Supports two call forms for minimal change:
|
|
140
|
+
* - withTimeout(promise) for preflight (SDK does not expose fetchImpl/signal)
|
|
141
|
+
* - withTimeout((signal) => sdkCallWithFetchImpl(abortingFetch)) for trust/verify
|
|
142
|
+
* Uses AbortController + signal: abort() is called on timeout so that when caller
|
|
143
|
+
* wires the returned signal into fetchImpl, the underlying network request is aborted.
|
|
144
|
+
* Always clears timer in finally (no leak). Attaches rejection observer to the
|
|
145
|
+
* call promise (without altering settlement) to avoid unhandled rejections on the
|
|
146
|
+
* loser of the race.
|
|
147
|
+
* Applied to actions + getIntelClient delegates.
|
|
148
|
+
*/
|
|
149
|
+
export function withTimeout(pOrMake, ms = 8000) {
|
|
150
|
+
if (typeof pOrMake === 'function') {
|
|
151
|
+
const makeCall = pOrMake;
|
|
152
|
+
const controller = new AbortController();
|
|
153
|
+
let timer;
|
|
154
|
+
const timeoutP = new Promise((_, reject) => {
|
|
155
|
+
timer = setTimeout(() => {
|
|
156
|
+
controller.abort();
|
|
157
|
+
reject(new Error('timeout'));
|
|
158
|
+
}, ms);
|
|
159
|
+
});
|
|
160
|
+
const callP = makeCall(controller.signal);
|
|
161
|
+
// Observe rejections on callP so a late rejection after timeout does not emit unhandledrejection.
|
|
162
|
+
callP.then(undefined, () => { });
|
|
163
|
+
return Promise.race([callP, timeoutP]).finally(() => {
|
|
164
|
+
if (timer !== undefined)
|
|
165
|
+
clearTimeout(timer);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
// legacy path (promise passed directly): still safe timeout + no unhandled + clear
|
|
169
|
+
const p = pOrMake;
|
|
170
|
+
let timer;
|
|
171
|
+
const timeoutP = new Promise((_, reject) => {
|
|
172
|
+
timer = setTimeout(() => {
|
|
173
|
+
reject(new Error('timeout'));
|
|
174
|
+
}, ms);
|
|
175
|
+
});
|
|
176
|
+
p.then(undefined, () => { });
|
|
177
|
+
return Promise.race([p, timeoutP]).finally(() => {
|
|
178
|
+
if (timer !== undefined)
|
|
179
|
+
clearTimeout(timer);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** x402-capable fetch injection for paid intel actions. No embedded wallet. */
|
|
2
|
+
import type { IAgentRuntime } from '@elizaos/core';
|
|
3
|
+
/** Host supplies an x402-capable fetch (agentcash, twzrd-x402-gate, etc.) before runtime creation. */
|
|
4
|
+
export declare function setPayingFetch(f: typeof fetch): void;
|
|
5
|
+
export declare function clearPayingFetch(): void;
|
|
6
|
+
type FetchLike = typeof fetch;
|
|
7
|
+
/** Resolve paying fetch: module setter > runtime service > runtime.fetch > global fetch. */
|
|
8
|
+
export declare function resolvePayingFetch(runtime: IAgentRuntime): FetchLike;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
let modulePayingFetch = null;
|
|
2
|
+
/** Host supplies an x402-capable fetch (agentcash, twzrd-x402-gate, etc.) before runtime creation. */
|
|
3
|
+
export function setPayingFetch(f) {
|
|
4
|
+
modulePayingFetch = f;
|
|
5
|
+
}
|
|
6
|
+
export function clearPayingFetch() {
|
|
7
|
+
modulePayingFetch = null;
|
|
8
|
+
}
|
|
9
|
+
function serviceFetch(runtime) {
|
|
10
|
+
for (const name of ['x402', 'payingFetch', 'agentcash']) {
|
|
11
|
+
const svc = runtime.getService(name);
|
|
12
|
+
if (svc?.fetch && typeof svc.fetch === 'function')
|
|
13
|
+
return svc.fetch;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
/** Resolve paying fetch: module setter > runtime service > runtime.fetch > global fetch. */
|
|
18
|
+
export function resolvePayingFetch(runtime) {
|
|
19
|
+
if (modulePayingFetch)
|
|
20
|
+
return modulePayingFetch;
|
|
21
|
+
const fromSvc = serviceFetch(runtime);
|
|
22
|
+
if (fromSvc)
|
|
23
|
+
return fromSvc;
|
|
24
|
+
if (runtime.fetch && typeof runtime.fetch === 'function')
|
|
25
|
+
return runtime.fetch;
|
|
26
|
+
return globalThis.fetch;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real E2E: load wzrdPlugin via @elizaos/core AgentRuntime (critical missing piece per plan).
|
|
3
|
+
* Constructs minimal runtime with plugins:[wzrdPlugin]; asserts intel action names present;
|
|
4
|
+
* invokes handlers w/ mock Memory (preflight uses seller_wallet/price/resource_name);
|
|
5
|
+
* paid path: injected mock paying fetch that returns fake IntelTrustResponse w/ receipt;
|
|
6
|
+
* verifies callback contains decision/score/ALLOW etc + success result.
|
|
7
|
+
* (Uses package import to match existing tsconfig.test paths + `npm run build && npm test` runtime;
|
|
8
|
+
* plan referenced '../src/index.js' for source-load semantics in test source.)
|
|
9
|
+
*/
|
|
10
|
+
import assert from 'node:assert/strict';
|
|
11
|
+
import { describe, it, before, after } from 'node:test';
|
|
12
|
+
import { AgentRuntime } from '@elizaos/core';
|
|
13
|
+
import wzrdPlugin, { intelPreflightAction, intelTrustAction, verifyReceiptAction, setPayingFetch, clearPayingFetch, getIntelClient, resolvePayingFetch, } from '@wzrd_sol/eliza-plugin';
|
|
14
|
+
function mockMemory(content) {
|
|
15
|
+
return {
|
|
16
|
+
id: '00000000-0000-0000-0000-000000000001',
|
|
17
|
+
entityId: '00000000-0000-0000-0000-000000000002',
|
|
18
|
+
roomId: '00000000-0000-0000-0000-000000000003',
|
|
19
|
+
content: content,
|
|
20
|
+
createdAt: Date.now(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function mockRuntime(settings = {}) {
|
|
24
|
+
const store = new Map(Object.entries(settings));
|
|
25
|
+
return {
|
|
26
|
+
getSetting: (key) => store.get(key) ?? null,
|
|
27
|
+
getService: () => null,
|
|
28
|
+
fetch: globalThis.fetch,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
describe('wzrdPlugin intel registration + E2E (real runtime load)', () => {
|
|
32
|
+
let runtime;
|
|
33
|
+
before(async () => {
|
|
34
|
+
runtime = new AgentRuntime({
|
|
35
|
+
character: { name: 'wzrd-intel-test', bio: 'test', plugins: [] },
|
|
36
|
+
settings: { WZRD_INTEL_URL: 'https://intel.twzrd.xyz' },
|
|
37
|
+
});
|
|
38
|
+
await runtime.registerPlugin(wzrdPlugin);
|
|
39
|
+
});
|
|
40
|
+
after(() => {
|
|
41
|
+
clearPayingFetch();
|
|
42
|
+
});
|
|
43
|
+
it('constructs runtime with plugins and registers intel actions', () => {
|
|
44
|
+
const names = runtime.actions.map((a) => a.name);
|
|
45
|
+
assert.ok(names.includes('WZRD_INTEL_PREFLIGHT'), 'preflight must be registered');
|
|
46
|
+
assert.ok(names.includes('WZRD_INTEL_TRUST'), 'trust must be registered');
|
|
47
|
+
assert.ok(names.includes('WZRD_VERIFY_RECEIPT'), 'verify must be registered');
|
|
48
|
+
});
|
|
49
|
+
it('preflight handler accepts seller_wallet + price + resource_name, returns success + decision/score text (live)', { timeout: 10000 }, async () => {
|
|
50
|
+
const rt = mockRuntime({ WZRD_INTEL_URL: 'https://intel.twzrd.xyz' });
|
|
51
|
+
const callbacks = [];
|
|
52
|
+
const result = await intelPreflightAction.handler(rt, mockMemory({
|
|
53
|
+
seller_wallet: '4LkEFjJdXARkKx8FBx4LBFa2SvJNmjQpgGDLoJcypZUE',
|
|
54
|
+
price_usdc: 0.05,
|
|
55
|
+
resource_name: 'test-resource',
|
|
56
|
+
}), undefined, undefined, async (r) => {
|
|
57
|
+
callbacks.push(r.text ?? '');
|
|
58
|
+
return [];
|
|
59
|
+
});
|
|
60
|
+
assert.equal(result?.success, true, `preflight failed: ${result?.error}`);
|
|
61
|
+
assert.equal(typeof result.success, 'boolean');
|
|
62
|
+
assert.ok(callbacks.length > 0);
|
|
63
|
+
const text = callbacks.join('\n');
|
|
64
|
+
assert.match(text, /Decision: (ALLOW|WARN|BLOCK)/i);
|
|
65
|
+
assert.match(text, /score|trust_score/i);
|
|
66
|
+
if (result.data)
|
|
67
|
+
assert.ok(result.data);
|
|
68
|
+
});
|
|
69
|
+
it('preflight validation rejects bad seller_wallet (non-base58)', async () => {
|
|
70
|
+
const rt = mockRuntime({ WZRD_INTEL_URL: 'https://intel.twzrd.xyz' });
|
|
71
|
+
const callbacks = [];
|
|
72
|
+
const result = await intelPreflightAction.handler(rt, mockMemory({ seller_wallet: 'not-base58!!!', price_usdc: 0.05 }), undefined, undefined, async (r) => {
|
|
73
|
+
callbacks.push(r.text ?? '');
|
|
74
|
+
return [];
|
|
75
|
+
});
|
|
76
|
+
assert.equal(result?.success, false);
|
|
77
|
+
assert.equal(typeof result.success, 'boolean');
|
|
78
|
+
const text = callbacks.join('\n');
|
|
79
|
+
assert.match(text, /Invalid preflight input/);
|
|
80
|
+
assert.match(text, /seller_wallet must be 32-44 char base58/);
|
|
81
|
+
});
|
|
82
|
+
it('preflight validation rejects negative price_usdc', async () => {
|
|
83
|
+
const rt = mockRuntime({ WZRD_INTEL_URL: 'https://intel.twzrd.xyz' });
|
|
84
|
+
const callbacks = [];
|
|
85
|
+
const result = await intelPreflightAction.handler(rt, mockMemory({ seller_wallet: '4LkEFjJdXARkKx8FBx4LBFa2SvJNmjQpgGDLoJcypZUE', price_usdc: -1 }), undefined, undefined, async (r) => {
|
|
86
|
+
callbacks.push(r.text ?? '');
|
|
87
|
+
return [];
|
|
88
|
+
});
|
|
89
|
+
assert.equal(result?.success, false);
|
|
90
|
+
assert.equal(typeof result.success, 'boolean');
|
|
91
|
+
const text = callbacks.join('\n');
|
|
92
|
+
assert.match(text, /Invalid preflight input/);
|
|
93
|
+
assert.match(text, /price_usdc must be non-negative finite number/);
|
|
94
|
+
});
|
|
95
|
+
it('trust handler with injected paying fetch (fake IntelTrustResponse containing receipt) succeeds + surfaces score + leaf in callback', async () => {
|
|
96
|
+
const fakeReceipt = {
|
|
97
|
+
version: 'v5',
|
|
98
|
+
leaf: '0xdeadbeef',
|
|
99
|
+
preimage: { domain: 'TWZRD:AO_REPUTATION_RECEIPT_V5', agent_id: '4LkEFjJdXARkKx8FBx4LBFa2SvJNmjQpgGDLoJcypZUE', score: 99, version: 'v5' },
|
|
100
|
+
signature: 'sig',
|
|
101
|
+
signing_pubkey: '11111111111111111111111111111111',
|
|
102
|
+
};
|
|
103
|
+
setPayingFetch(async () => ({
|
|
104
|
+
ok: true,
|
|
105
|
+
status: 200,
|
|
106
|
+
json: async () => ({ pubkey: '4LkEFjJdXARkKx8FBx4LBFa2SvJNmjQpgGDLoJcypZUE', trust: { score: 88 }, paid: true, twzrd_receipt: fakeReceipt }),
|
|
107
|
+
}));
|
|
108
|
+
const rt = mockRuntime();
|
|
109
|
+
const callbacks = [];
|
|
110
|
+
const result = await intelTrustAction.handler(rt, mockMemory({ pubkey: '4LkEFjJdXARkKx8FBx4LBFa2SvJNmjQpgGDLoJcypZUE' }), undefined, undefined, async (r) => {
|
|
111
|
+
callbacks.push(r.text ?? '');
|
|
112
|
+
return [];
|
|
113
|
+
});
|
|
114
|
+
assert.equal(result?.success, true, String(result?.error));
|
|
115
|
+
assert.equal(typeof result.success, 'boolean');
|
|
116
|
+
assert.ok(callbacks.length > 0);
|
|
117
|
+
const text = callbacks.join('\n');
|
|
118
|
+
assert.match(text, /Score: 88/);
|
|
119
|
+
assert.match(text, /leaf:/i);
|
|
120
|
+
if (result.data)
|
|
121
|
+
assert.ok(result.data);
|
|
122
|
+
});
|
|
123
|
+
it('PREFLIGHT-BEFORE-PAY: decision=block aborts the trust purchase before any payment', async () => {
|
|
124
|
+
// preSpendGate runs the FREE preflight via the SDK's intelPreflight, which uses
|
|
125
|
+
// the global fetch (not the injected paying fetch — that is reserved for the
|
|
126
|
+
// paid leg). So we stub global fetch to return a block ReadinessCard, and put a
|
|
127
|
+
// tripwire on the injected paying fetch: if the pay leg runs after a block, the
|
|
128
|
+
// tripwire fires and the test fails.
|
|
129
|
+
const realFetch = globalThis.fetch;
|
|
130
|
+
let payAttempted = false;
|
|
131
|
+
setPayingFetch(async () => {
|
|
132
|
+
payAttempted = true;
|
|
133
|
+
return { ok: true, status: 200, json: async () => ({}) };
|
|
134
|
+
});
|
|
135
|
+
globalThis.fetch = (async () => ({
|
|
136
|
+
ok: true,
|
|
137
|
+
status: 200,
|
|
138
|
+
json: async () => ({
|
|
139
|
+
readiness_card: { decision: 'block', trust_score: 5, can_spend: false },
|
|
140
|
+
reason: 'seller flagged by corpus',
|
|
141
|
+
}),
|
|
142
|
+
}));
|
|
143
|
+
try {
|
|
144
|
+
const callbacks = [];
|
|
145
|
+
const result = await intelTrustAction.handler(mockRuntime(), mockMemory({ pubkey: '4LkEFjJdXARkKx8FBx4LBFa2SvJNmjQpgGDLoJcypZUE' }), undefined, undefined, async (r) => {
|
|
146
|
+
callbacks.push(r.text ?? '');
|
|
147
|
+
return [];
|
|
148
|
+
});
|
|
149
|
+
assert.equal(result?.success, false);
|
|
150
|
+
assert.equal(result?.error, 'preflight_block');
|
|
151
|
+
assert.equal(payAttempted, false, 'payment must NOT be attempted after a block decision');
|
|
152
|
+
assert.match(callbacks.join('\n'), /blocked|No payment was sent/i);
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
globalThis.fetch = realFetch;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
it('VERIFY: a V6 receipt (reputation_* bound into the leaf) is accepted by the verify action path', async () => {
|
|
159
|
+
// Live receipts are V6; the SDK selects the leaf binding from the domain, so a
|
|
160
|
+
// V6 receipt must round-trip through WZRD_VERIFY_RECEIPT without a V5/V6 mixup.
|
|
161
|
+
// We assert the action runs and reports the V6 domain rather than erroring.
|
|
162
|
+
const v6Receipt = {
|
|
163
|
+
version: 'v6',
|
|
164
|
+
leaf: '0x4c82649d2be393b1fca2da7c5d4c7afebb189ad3f0b93b620ce2e552fe5ce558',
|
|
165
|
+
preimage: {
|
|
166
|
+
domain: 'TWZRD:AO_REPUTATION_RECEIPT_V6',
|
|
167
|
+
agent_id: '11111111111111111111111111111111',
|
|
168
|
+
score: 72,
|
|
169
|
+
confidence_bps: 8000,
|
|
170
|
+
timestamp_unix: 1748736000,
|
|
171
|
+
payer: '11111111111111111111111111111111',
|
|
172
|
+
settlement_tx: 'EXAMPLE-sample-receipt-no-real-settlement-tx-0001',
|
|
173
|
+
reputation_score: 4242,
|
|
174
|
+
reputation_confidence_bps: 7500,
|
|
175
|
+
reputation_score_version: 'intel_renorm_v1',
|
|
176
|
+
reputation_feature_window_start_unix: 1748000000,
|
|
177
|
+
reputation_data_quality: 'high',
|
|
178
|
+
version: 'v6',
|
|
179
|
+
},
|
|
180
|
+
signature: 'sig',
|
|
181
|
+
signing_pubkey: '11111111111111111111111111111111',
|
|
182
|
+
};
|
|
183
|
+
const callbacks = [];
|
|
184
|
+
const result = await verifyReceiptAction.handler(mockRuntime(), mockMemory({ receipt: v6Receipt }), undefined, undefined, async (r) => {
|
|
185
|
+
callbacks.push(r.text ?? '');
|
|
186
|
+
return [];
|
|
187
|
+
});
|
|
188
|
+
// The action ran (no exception) and exercised the SDK V6 leaf path. The
|
|
189
|
+
// signature is a stub so it won't pass authenticity, but the leaf must
|
|
190
|
+
// recompute against the V6 binding (not the V5 one) — proven by the SDK's
|
|
191
|
+
// own canonical-vector tests; here we assert the action did not crash and
|
|
192
|
+
// surfaced a verdict for the V6 domain.
|
|
193
|
+
assert.ok(result, 'verify action returned a result for a V6 receipt');
|
|
194
|
+
assert.ok(callbacks.length > 0, 'verify action produced output for a V6 receipt');
|
|
195
|
+
});
|
|
196
|
+
it('trust surfaces payment_required + x402/agentcash text on 402 from (failing) fetch (explicit IntelPaymentRequiredError branch)', async () => {
|
|
197
|
+
setPayingFetch(async () => ({
|
|
198
|
+
ok: false,
|
|
199
|
+
status: 402,
|
|
200
|
+
headers: { get: () => null },
|
|
201
|
+
json: async () => ({ error: 'payment required' }),
|
|
202
|
+
text: async () => '',
|
|
203
|
+
}));
|
|
204
|
+
const rt = mockRuntime();
|
|
205
|
+
const callbacks = [];
|
|
206
|
+
const result = await intelTrustAction.handler(rt, mockMemory({ pubkey: '4LkEFjJdXARkKx8FBx4LBFa2SvJNmjQpgGDLoJcypZUE' }), undefined, undefined, async (r) => {
|
|
207
|
+
callbacks.push(r.text ?? '');
|
|
208
|
+
return [];
|
|
209
|
+
});
|
|
210
|
+
assert.equal(result?.success, false);
|
|
211
|
+
assert.equal(result?.error, 'payment_required');
|
|
212
|
+
assert.equal(typeof result.success, 'boolean');
|
|
213
|
+
const text = callbacks.join('\n');
|
|
214
|
+
assert.match(text, /x402|Intel trust requires|agentcash/i);
|
|
215
|
+
});
|
|
216
|
+
it('verify handler runs with receipt content (exercises SDK verify path)', async () => {
|
|
217
|
+
const rt = mockRuntime();
|
|
218
|
+
const callbacks = [];
|
|
219
|
+
const minimalReceipt = {
|
|
220
|
+
version: 'v5',
|
|
221
|
+
leaf: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
222
|
+
preimage: { domain: 'TWZRD:AO_REPUTATION_RECEIPT_V5', agent_id: '4LkEFjJdXARkKx8FBx4LBFa2SvJNmjQpgGDLoJcypZUE', score: 0, version: 'v5' },
|
|
223
|
+
signature: 'AA',
|
|
224
|
+
signing_pubkey: '11111111111111111111111111111111',
|
|
225
|
+
};
|
|
226
|
+
const result = await verifyReceiptAction.handler(rt, mockMemory({ receipt: minimalReceipt }), undefined, undefined, async (r) => {
|
|
227
|
+
callbacks.push(r.text ?? '');
|
|
228
|
+
return [];
|
|
229
|
+
});
|
|
230
|
+
assert.ok(result, 'verify handler must return');
|
|
231
|
+
assert.equal(typeof result.success, 'boolean');
|
|
232
|
+
assert.equal(result.success, false);
|
|
233
|
+
assert.ok(callbacks.length > 0);
|
|
234
|
+
const text = callbacks.join('\n');
|
|
235
|
+
assert.match(text, /INVALID/);
|
|
236
|
+
});
|
|
237
|
+
it('getIntelClient factory + resolvePayingFetch service fallback exercised (covers factory delegates and resolve branches)', () => {
|
|
238
|
+
clearPayingFetch(); // ensure module-level does not shadow the service fallback we want to test
|
|
239
|
+
const mockPaying = async () => ({ ok: true, status: 200, json: async () => ({}) });
|
|
240
|
+
const rt = {
|
|
241
|
+
getSetting: () => null,
|
|
242
|
+
getService: (n) => (n === 'payingFetch' ? { fetch: mockPaying } : null),
|
|
243
|
+
fetch: globalThis.fetch,
|
|
244
|
+
};
|
|
245
|
+
const intel = getIntelClient(rt);
|
|
246
|
+
assert.equal(typeof intel.preflight, 'function');
|
|
247
|
+
assert.equal(typeof intel.trust, 'function');
|
|
248
|
+
assert.equal(typeof intel.verify, 'function');
|
|
249
|
+
// exercise at least one resolve fallback path (service)
|
|
250
|
+
const resolved = resolvePayingFetch(rt);
|
|
251
|
+
assert.equal(resolved, mockPaying);
|
|
252
|
+
});
|
|
253
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wzrd_sol/eliza-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "WZRD
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "WZRD Agent Intel for ElizaOS — preflight ReadinessCards, x402-paid V5 trust receipts, offline verification on intel.twzrd.xyz. Also ships the legacy earn loop.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "tsc",
|
|
17
|
-
"test": "node --test dist/test
|
|
16
|
+
"build": "tsc && tsc -p tsconfig.test.json",
|
|
17
|
+
"test": "node --test dist/test/plugin-registration.intel.js",
|
|
18
18
|
"prepublishOnly": "npm run build"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@solana/web3.js": "^1.95.0",
|
|
22
|
+
"@wzrd_sol/sdk": "^0.4.3",
|
|
22
23
|
"bs58": "^5.0.0",
|
|
23
24
|
"tweetnacl": "^1.0.3"
|
|
24
25
|
},
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@elizaos/core": "^1.0.0",
|
|
36
|
+
"@types/node": "^22.13.10",
|
|
35
37
|
"typescript": "^5.6.3"
|
|
36
38
|
},
|
|
37
39
|
"keywords": [
|
|
@@ -39,6 +41,12 @@
|
|
|
39
41
|
"eliza",
|
|
40
42
|
"elizaos",
|
|
41
43
|
"wzrd",
|
|
44
|
+
"intel",
|
|
45
|
+
"x402",
|
|
46
|
+
"trust",
|
|
47
|
+
"receipt",
|
|
48
|
+
"reputation",
|
|
49
|
+
"preflight",
|
|
42
50
|
"ccm",
|
|
43
51
|
"inference",
|
|
44
52
|
"earn",
|