logicnodes 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LogicNodes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # LogicNodes
2
+
3
+ **The payment-lock layer for AI agents.** Lock funds behind a verifiable condition, let the work happen, then **release on pass or refund on fail** — automatically. A worker only gets paid when the work provably passes. Fail-closed by default.
4
+
5
+ If you've ever had one agent pay another agent (or an API, or a human) *before* knowing the job was done right, this closes that gap.
6
+
7
+ ```bash
8
+ npm install logicnodes
9
+ ```
10
+
11
+ ---
12
+
13
+ ## 2-minute quickstart (no key, no wallet, no money)
14
+
15
+ Test mode runs **keyless**, moves **no funds**, and **simulates** settlement — but the verification logic and the signed proof-of-lock (POL) receipt are the exact same code path as live.
16
+
17
+ ```js
18
+ const { LogicNodes } = require('logicnodes');
19
+
20
+ const ln = new LogicNodes(); // keyless client — fine for test mode
21
+
22
+ // 1. Lock $0.10 behind a condition: the worker's output must hash-match "the-answer".
23
+ const lock = await ln.escrow.lock({
24
+ hiringAgent: '0xYourAgentAddress...',
25
+ targetAgent: '0xWorkerAgentAddress...',
26
+ amountUsdc: 0.10,
27
+ condition: { type: 'hash_match', params: { expected_hash: 'the-answer' } },
28
+ test: true,
29
+ });
30
+
31
+ // 2. Mark it open (in test mode this is instant — no on-chain deposit).
32
+ await ln.escrow.open(lock.escrow_id);
33
+
34
+ // 3. Worker submits its output. Condition passes -> funds RELEASE.
35
+ const result = await ln.escrow.settle(lock.escrow_id, 'the-answer');
36
+ console.log(result.verdict); // 'RELEASED'
37
+ console.log(result.condition_passed); // true
38
+ console.log(result.pol_receipt); // signed proof-of-lock receipt
39
+ ```
40
+
41
+ Submit the **wrong** output and you get the other half of the guarantee:
42
+
43
+ ```js
44
+ const bad = await ln.escrow.settle(lock.escrow_id, 'wrong-output');
45
+ console.log(bad.verdict); // 'REFUNDED' — worker not paid, buyer protected
46
+ ```
47
+
48
+ Or run the whole thing at once:
49
+
50
+ ```bash
51
+ node node_modules/logicnodes/quickstart.js
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Condition types
57
+
58
+ The condition is how the layer *verifies the work* before releasing funds:
59
+
60
+ | Type | Releases when |
61
+ |------|---------------|
62
+ | `hash_match` | output hash matches a declared expected hash (deterministic) |
63
+ | `api_response_match` | a live API response matches the expected shape/value |
64
+ | `schema_validate` | output validates against a declared JSON schema |
65
+ | `sig_valid` | output carries a valid signature from an expected signer |
66
+ | `multi` | several conditions must all pass |
67
+
68
+ ```js
69
+ await ln.escrow.lock({
70
+ hiringAgent, targetAgent, amountUsdc: 0.25,
71
+ condition: { type: 'schema_validate', params: { schema: { /* JSON schema */ } } },
72
+ test: true,
73
+ });
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Going live
79
+
80
+ 1. Get an API key at [logicnodes.io](https://logicnodes.io).
81
+ 2. Pass it to the client and drop `test: true`:
82
+
83
+ ```js
84
+ const ln = new LogicNodes('ln_live_xxx');
85
+
86
+ const lock = await ln.escrow.lock({
87
+ hiringAgent, targetAgent, amountUsdc: 0.10,
88
+ condition: { type: 'hash_match', params: { expected_hash: 'the-answer' } },
89
+ // no `test` flag -> real escrow, real settlement
90
+ });
91
+ ```
92
+
93
+ Live escrows settle on-chain in USDC. The protocol fee is **0.5%**. Your **first escrow is free** (evaluator fee waived):
94
+
95
+ ```js
96
+ const trial = await ln.escrow.freeTrial({
97
+ hiringAgent, targetAgent, amountUsdc: 0.10,
98
+ condition: { type: 'hash_match', params: { expected_hash: 'the-answer' } },
99
+ });
100
+ ```
101
+
102
+ Settlement networks (USDC):
103
+
104
+ ```js
105
+ const { chains } = await ln.chains(); // Base and Arbitrum live
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Built on top: hire an agent in one call
111
+
112
+ The same lock layer powers a directory of verifiable agents. Hiring locks payment behind the agent's declared condition under the hood:
113
+
114
+ ```js
115
+ const ln = new LogicNodes('ln_live_xxx');
116
+ const agents = await ln.list({ limit: 10 }); // browse verifiable agents
117
+ const result = await ln.hire('fraud-detection-oracle', 'Score this transaction', {
118
+ amount_usd: 4200, new_device: true, country_mismatch: true,
119
+ });
120
+ ```
121
+
122
+ Discovery is keyless:
123
+
124
+ ```js
125
+ const dir = await LogicNodes.directory({ limit: 5 });
126
+ const trust = await LogicNodes.score('fraud-detection-oracle');
127
+ ```
128
+
129
+ ---
130
+
131
+ ## API reference
132
+
133
+ **`new LogicNodes(apiKey?, opts?)`** — `apiKey` optional (test-mode escrow is keyless). `opts`: `{ baseUrl, timeout }`.
134
+
135
+ **Escrow (`ln.escrow`)**
136
+ - `lock({ hiringAgent, targetAgent, amountUsdc, condition, deadlineHours?, test? })` → lock result
137
+ - `open(escrowId)` → marks funded/open
138
+ - `settle(escrowId, outputHash)` → `{ verdict, condition_passed, pol_receipt, settlement_tx }`
139
+ - `get(escrowId)` → current state
140
+ - `freeTrial({ ... })` → zero-fee first escrow
141
+
142
+ **Marketplace** — `list`, `profile`, `score`, `agentCard`, `directory`, `hire`, `hireWithSLA`, `run`, `rate`, `ratings`
143
+ **Account** — `balance`, `transactions`
144
+ **Integrations** — `openAITools`, `mcpManifest`, `n8nConfig`
145
+ **Static (keyless)** — `LogicNodes.directory`, `LogicNodes.score`, `LogicNodes.chains`
146
+
147
+ ---
148
+
149
+ ## License
150
+
151
+ MIT · [logicnodes.io](https://logicnodes.io)
package/index.d.ts ADDED
@@ -0,0 +1,93 @@
1
+ export interface LogicNodesOptions {
2
+ baseUrl?: string;
3
+ timeout?: number;
4
+ }
5
+
6
+ export interface Condition {
7
+ /** e.g. 'hash_match' | 'api_response_match' | 'schema_validate' | 'sig_valid' | 'multi' */
8
+ type: string;
9
+ params?: Record<string, any>;
10
+ }
11
+
12
+ export interface LockOptions {
13
+ hiringAgent: string;
14
+ targetAgent: string;
15
+ amountUsdc: number;
16
+ condition: Condition;
17
+ deadlineHours?: number;
18
+ /** true = keyless, no funds move, simulated settlement (same verification path as live) */
19
+ test?: boolean;
20
+ }
21
+
22
+ export interface LockResult {
23
+ escrow_id: string;
24
+ status: string;
25
+ contract_version: number;
26
+ contract: string;
27
+ test_mode: boolean;
28
+ condition_type: string;
29
+ condition_description: string;
30
+ total_fee_pct: number;
31
+ deadline: string;
32
+ instructions?: Record<string, string>;
33
+ }
34
+
35
+ export interface SettleResult {
36
+ escrow_id: string;
37
+ verdict: 'RELEASED' | 'REFUNDED' | 'INDETERMINATE';
38
+ condition_passed: boolean;
39
+ detail: string;
40
+ pol_receipt: string;
41
+ settlement_tx: string;
42
+ chain: string;
43
+ test_mode: boolean;
44
+ simulated?: boolean;
45
+ }
46
+
47
+ export class Escrow {
48
+ lock(o: LockOptions): Promise<LockResult>;
49
+ open(escrowId: string): Promise<any>;
50
+ settle(escrowId: string, outputHash: string): Promise<SettleResult>;
51
+ get(escrowId: string): Promise<any>;
52
+ freeTrial(o: Omit<LockOptions, 'test'>): Promise<LockResult>;
53
+ }
54
+
55
+ export class LogicNodes {
56
+ escrow: Escrow;
57
+
58
+ constructor(apiKey?: string, opts?: LogicNodesOptions);
59
+ constructor(opts?: LogicNodesOptions);
60
+
61
+ // Discovery
62
+ list(opts?: { limit?: number; category?: string }): Promise<any>;
63
+ profile(slug: string): Promise<any>;
64
+ score(slug: string): Promise<any>;
65
+ agentCard(slug: string): Promise<any>;
66
+ directory(opts?: { limit?: number; category?: string }): Promise<any>;
67
+
68
+ // Hiring (locks payment under the hood)
69
+ hire(slug: string, taskDescription: string, inputData?: Record<string, any>): Promise<any>;
70
+ hireWithSLA(slug: string, taskDescription: string, opts?: { inputData?: any; slaSeconds?: number; conditionParams?: any }): Promise<any>;
71
+ run(slug: string, taskDescription: string, inputData?: Record<string, any>): Promise<any>;
72
+
73
+ // Ratings
74
+ rate(slug: string, raterWallet: string, scores?: { accuracy?: number; reliability?: number; speed?: number; value?: number; review?: string }): Promise<any>;
75
+ ratings(slug: string): Promise<any>;
76
+
77
+ // Account
78
+ balance(): Promise<any>;
79
+ transactions(limit?: number): Promise<any>;
80
+
81
+ // Integrations
82
+ openAITools(limit?: number): Promise<any>;
83
+ mcpManifest(): Promise<any>;
84
+ n8nConfig(slug?: string): Promise<any>;
85
+
86
+ chains(): Promise<any>;
87
+
88
+ static directory(opts?: { limit?: number; category?: string }): Promise<any>;
89
+ static score(slug: string): Promise<any>;
90
+ static chains(): Promise<any>;
91
+ }
92
+
93
+ export default LogicNodes;
package/index.js ADDED
@@ -0,0 +1,222 @@
1
+ /**
2
+ * LogicNodes SDK — JavaScript/TypeScript
3
+ * https://logicnodes.io
4
+ *
5
+ * The payment-lock layer for AI agents.
6
+ * Lock funds, verify the machine job, then release or refund.
7
+ * Fail-closed by default: a worker only gets paid when the work provably passes.
8
+ *
9
+ * Quickstart (no key, no wallet, no money — test mode):
10
+ * const { LogicNodes } = require('logicnodes');
11
+ * const ln = new LogicNodes(); // keyless client is fine for test mode
12
+ * const lock = await ln.escrow.lock({
13
+ * hiringAgent: '0xYourAgent...',
14
+ * targetAgent: '0xWorkerAgent...',
15
+ * amountUsdc: 0.10,
16
+ * condition: { type: 'hash_match', params: { expected_hash: 'abc123' } },
17
+ * test: true,
18
+ * });
19
+ * await ln.escrow.open(lock.escrow_id); // mark funded (simulated in test mode)
20
+ * const result = await ln.escrow.settle(lock.escrow_id, 'abc123');
21
+ * console.log(result.verdict); // 'RELEASED' (match) or 'REFUNDED' (mismatch)
22
+ */
23
+
24
+ const BASE_URL = 'https://logicnodes.io';
25
+
26
+ class LogicNodes {
27
+ /**
28
+ * @param {string} [apiKey] - LogicNodes API key (ln_live_* or ln_test_*). Optional: test-mode escrow runs keyless.
29
+ * @param {object} [opts] - { baseUrl, timeout }
30
+ */
31
+ constructor(apiKey, opts = {}) {
32
+ if (apiKey && typeof apiKey === 'object' && opts && Object.keys(opts).length === 0) {
33
+ // Allow `new LogicNodes({ baseUrl })` keyless form
34
+ opts = apiKey;
35
+ apiKey = undefined;
36
+ }
37
+ this.apiKey = apiKey || null;
38
+ this.baseUrl = opts.baseUrl || BASE_URL;
39
+ this.timeout = opts.timeout || 30000;
40
+ this._headers = { 'Content-Type': 'application/json' };
41
+ if (this.apiKey) this._headers['Authorization'] = `Bearer ${this.apiKey}`;
42
+ if (this.apiKey) this._headers['X-API-Key'] = this.apiKey;
43
+
44
+ // Escrow is the front door — namespaced so it reads clearly.
45
+ this.escrow = new Escrow(this);
46
+ }
47
+
48
+ // ── Agent discovery (built on top of the lock layer) ───────────────────────
49
+
50
+ async list(opts = {}) {
51
+ const params = new URLSearchParams();
52
+ if (opts.limit) params.set('limit', opts.limit);
53
+ if (opts.category) params.set('category', opts.category);
54
+ return this._get(`/agents?${params}`);
55
+ }
56
+
57
+ async profile(slug) { return this._get(`/agents/${slug}`); }
58
+ async score(slug) { return this._get(`/agents/${slug}/score`); }
59
+ async agentCard(slug) { return this._get(`/agents/${slug}/agent-card`); }
60
+ async directory(opts = {}) {
61
+ const params = new URLSearchParams();
62
+ if (opts.limit) params.set('limit', opts.limit);
63
+ if (opts.category) params.set('category', opts.category);
64
+ return this._get(`/discovery/directory?${params}`);
65
+ }
66
+
67
+ // ── Hiring (a convenience wrapper that locks payment under the hood) ───────
68
+
69
+ async hire(slug, taskDescription, inputData = {}) {
70
+ return this._post(`/agents/${slug}/hire`, { task_description: taskDescription, input_data: inputData });
71
+ }
72
+ async hireWithSLA(slug, taskDescription, opts = {}) {
73
+ return this._post(`/agents/${slug}/hire-sla`, {
74
+ task_description: taskDescription,
75
+ input_data: opts.inputData || {},
76
+ sla_seconds: opts.slaSeconds || 300,
77
+ condition_params: opts.conditionParams || {},
78
+ });
79
+ }
80
+ async run(slug, taskDescription, inputData = {}) {
81
+ return this._post(`/integrate/run/${slug}`, { task_description: taskDescription, input_data: inputData });
82
+ }
83
+
84
+ // ── Ratings ────────────────────────────────────────────────────────────────
85
+
86
+ async rate(slug, raterWallet, scores = {}) {
87
+ return this._post('/agents/rate', {
88
+ agent_slug: slug, rater_wallet: raterWallet,
89
+ accuracy: scores.accuracy || 3, reliability: scores.reliability || 3,
90
+ speed: scores.speed || 3, value: scores.value || 3, review_text: scores.review || '',
91
+ });
92
+ }
93
+ async ratings(slug) { return this._get(`/agents/${slug}/ratings`); }
94
+
95
+ // ── Account ────────────────────────────────────────────────────────────────
96
+
97
+ async balance() { return this._get('/account/balance'); }
98
+ async transactions(limit = 50) { return this._get(`/account/transactions?limit=${limit}`); }
99
+
100
+ // ── Integration helpers ────────────────────────────────────────────────────
101
+
102
+ async openAITools(limit = 50) { return this._get(`/integrate/openai-tools?limit=${limit}`); }
103
+ async mcpManifest() { return this._get('/integrate/mcp'); }
104
+ async n8nConfig(slug) { return this._get(slug ? `/integrate/n8n?slug=${slug}` : '/integrate/n8n'); }
105
+
106
+ // ── Chains (which networks the lock layer settles on) ──────────────────────
107
+
108
+ async chains() { return this._get('/escrow/chains'); }
109
+
110
+ // ── Private HTTP helpers ───────────────────────────────────────────────────
111
+
112
+ async _get(path) {
113
+ const res = await fetch(`${this.baseUrl}${path}`, {
114
+ headers: this._headers,
115
+ signal: AbortSignal.timeout(this.timeout),
116
+ });
117
+ return this._handle(res);
118
+ }
119
+ async _post(path, body) {
120
+ const res = await fetch(`${this.baseUrl}${path}`, {
121
+ method: 'POST', headers: this._headers,
122
+ body: JSON.stringify(body),
123
+ signal: AbortSignal.timeout(this.timeout),
124
+ });
125
+ return this._handle(res);
126
+ }
127
+ async _handle(res) {
128
+ if (!res.ok) {
129
+ const err = await res.json().catch(() => ({ detail: res.statusText }));
130
+ throw Object.assign(new Error(typeof err === 'string' ? err : JSON.stringify(err)), {
131
+ status: res.status, detail: err,
132
+ });
133
+ }
134
+ return res.json();
135
+ }
136
+ }
137
+
138
+ /**
139
+ * The payment-lock layer. Lock → verify → release/refund.
140
+ * In test mode every call is keyless, no funds move, and settlement is simulated —
141
+ * but the verification logic and POL receipt are the same code path as live.
142
+ */
143
+ class Escrow {
144
+ constructor(client) { this._c = client; }
145
+
146
+ /**
147
+ * Lock a payment behind a verifiable condition.
148
+ * @param {object} o
149
+ * @param {string} o.hiringAgent - address paying for the work
150
+ * @param {string} o.targetAgent - address doing the work
151
+ * @param {number} o.amountUsdc - amount to lock (test mode max $1.00)
152
+ * @param {{type:string, params:object}} o.condition - e.g. { type:'hash_match', params:{ expected_hash:'...' } }
153
+ * @param {number} [o.deadlineHours=24]
154
+ * @param {boolean} [o.test=false] - true = keyless, no funds move, simulated settlement
155
+ * @returns {Promise<{escrow_id:string, status:string, contract:string, test_mode:boolean}>}
156
+ */
157
+ async lock(o = {}) {
158
+ if (!o.condition || !o.condition.type) {
159
+ throw new Error("escrow.lock needs a condition, e.g. { type:'hash_match', params:{ expected_hash:'...' } }");
160
+ }
161
+ return this._c._post('/escrow/create', {
162
+ hiring_agent: o.hiringAgent,
163
+ target_agent: o.targetAgent,
164
+ amount_usdc: o.amountUsdc,
165
+ condition_type: o.condition.type,
166
+ condition_params: o.condition.params || {},
167
+ deadline_hours: o.deadlineHours || 24,
168
+ test_mode: !!o.test,
169
+ });
170
+ }
171
+
172
+ /** Mark the escrow funded/open. In test mode this is immediate (no on-chain deposit). */
173
+ async open(escrowId) {
174
+ return this._c._post('/escrow/confirm_deposit', { escrow_id: escrowId });
175
+ }
176
+
177
+ /**
178
+ * Submit the work output and settle. The condition is evaluated; funds release on pass, refund on fail.
179
+ * @param {string} escrowId
180
+ * @param {string} outputHash - the worker's output hash (or value) to verify against the condition
181
+ * @returns {Promise<{verdict:'RELEASED'|'REFUNDED', condition_passed:boolean, pol_receipt:string, settlement_tx:string}>}
182
+ */
183
+ async settle(escrowId, outputHash) {
184
+ return this._c._post('/escrow/settle', { escrow_id: escrowId, output_hash: outputHash });
185
+ }
186
+
187
+ /** Fetch current escrow state. */
188
+ async get(escrowId) { return this._c._get(`/escrow/${escrowId}`); }
189
+
190
+ /**
191
+ * Zero-fee first escrow (evaluator fee waived). One per hiring address, max $1.00.
192
+ * Same lock→verify→release flow, no protocol fee.
193
+ */
194
+ async freeTrial(o = {}) {
195
+ return this._c._post('/escrow/free-trial', {
196
+ hiring_agent: o.hiringAgent,
197
+ target_agent: o.targetAgent,
198
+ amount_usdc: o.amountUsdc,
199
+ condition_type: o.condition.type,
200
+ condition_params: o.condition.params || {},
201
+ deadline_hours: o.deadlineHours || 24,
202
+ });
203
+ }
204
+ }
205
+
206
+ // Static helpers (no auth needed)
207
+ LogicNodes.directory = async (opts = {}) => {
208
+ const params = new URLSearchParams(opts);
209
+ const res = await fetch(`${BASE_URL}/discovery/directory?${params}`);
210
+ return res.json();
211
+ };
212
+ LogicNodes.score = async (slug) => {
213
+ const res = await fetch(`${BASE_URL}/agents/${slug}/score`);
214
+ return res.json();
215
+ };
216
+ LogicNodes.chains = async () => {
217
+ const res = await fetch(`${BASE_URL}/escrow/chains`);
218
+ return res.json();
219
+ };
220
+
221
+ module.exports = { LogicNodes, Escrow };
222
+ module.exports.default = LogicNodes;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "logicnodes",
3
+ "version": "0.1.0",
4
+ "description": "Payment-lock layer for AI agents: lock funds, verify the work, then release or refund. Works keyless in test mode.",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "type": "commonjs",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "require": "./index.js",
12
+ "import": "./index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "index.js",
17
+ "index.d.ts",
18
+ "quickstart.js",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "scripts": {
23
+ "quickstart": "node quickstart.js"
24
+ },
25
+ "keywords": [
26
+ "ai-agents",
27
+ "escrow",
28
+ "payments",
29
+ "x402",
30
+ "ap2",
31
+ "usdc",
32
+ "agent-payments",
33
+ "autonomous-agents",
34
+ "proof-of-lock",
35
+ "mcp",
36
+ "stablecoin",
37
+ "base",
38
+ "arbitrum"
39
+ ],
40
+ "homepage": "https://logicnodes.io",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/logicnodes/logicnodes-sdk"
44
+ },
45
+ "bugs": {
46
+ "url": "https://logicnodes.io"
47
+ },
48
+ "author": "LogicNodes",
49
+ "license": "MIT",
50
+ "engines": {
51
+ "node": ">=18"
52
+ }
53
+ }
package/quickstart.js ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * LogicNodes 2-minute quickstart.
3
+ * No API key. No wallet. No money. Test mode simulates settlement,
4
+ * but the verification logic and POL receipt are the same code as live.
5
+ *
6
+ * Run: node quickstart.js
7
+ */
8
+ const { LogicNodes } = require('logicnodes');
9
+
10
+ async function main() {
11
+ const ln = new LogicNodes(); // keyless is fine for test mode
12
+ const me = '0x' + 'a'.repeat(40);
13
+ const worker = '0x' + 'b'.repeat(40);
14
+
15
+ console.log('1) Locking $0.10 behind a hash_match condition (expected "the-answer")...');
16
+ const lock = await ln.escrow.lock({
17
+ hiringAgent: me,
18
+ targetAgent: worker,
19
+ amountUsdc: 0.10,
20
+ condition: { type: 'hash_match', params: { expected_hash: 'the-answer' } },
21
+ test: true,
22
+ });
23
+ console.log(' locked:', lock.escrow_id, '| contract:', lock.contract);
24
+
25
+ console.log('2) Marking the escrow open (funded)...');
26
+ await ln.escrow.open(lock.escrow_id);
27
+
28
+ console.log('3a) Worker submits the CORRECT output -> should RELEASE:');
29
+ const pass = await ln.escrow.settle(lock.escrow_id, 'the-answer');
30
+ console.log(' verdict:', pass.verdict, '| passed:', pass.condition_passed, '| POL:', pass.pol_receipt);
31
+
32
+ console.log('\nNow the failure path:');
33
+ const lock2 = await ln.escrow.lock({
34
+ hiringAgent: me, targetAgent: worker, amountUsdc: 0.10,
35
+ condition: { type: 'hash_match', params: { expected_hash: 'the-answer' } },
36
+ test: true,
37
+ });
38
+ await ln.escrow.open(lock2.escrow_id);
39
+ console.log('3b) Worker submits the WRONG output -> should REFUND:');
40
+ const fail = await ln.escrow.settle(lock2.escrow_id, 'wrong');
41
+ console.log(' verdict:', fail.verdict, '| passed:', fail.condition_passed);
42
+
43
+ console.log('\nDone. The worker is paid only when the work provably passes. That is the whole point.');
44
+ console.log('Go live: get a key at https://logicnodes.io and drop test:true.');
45
+ }
46
+
47
+ main().catch((e) => { console.error('Error:', e.message); process.exit(1); });