n8n-nodes-taliware 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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +291 -0
  3. package/dist/credentials/TaliwareApi.credentials.d.ts +9 -0
  4. package/dist/credentials/TaliwareApi.credentials.js +52 -0
  5. package/dist/credentials/TaliwareApi.credentials.js.map +1 -0
  6. package/dist/credentials/icons/taliware.dark.svg +13 -0
  7. package/dist/credentials/icons/taliware.svg +13 -0
  8. package/dist/lib/cl2cClient.d.ts +18 -0
  9. package/dist/lib/cl2cClient.js +77 -0
  10. package/dist/lib/cl2cClient.js.map +1 -0
  11. package/dist/lib/createNft.d.ts +12 -0
  12. package/dist/lib/createNft.js +36 -0
  13. package/dist/lib/createNft.js.map +1 -0
  14. package/dist/lib/errors.d.ts +16 -0
  15. package/dist/lib/errors.js +68 -0
  16. package/dist/lib/errors.js.map +1 -0
  17. package/dist/lib/getTaliwareCredentials.d.ts +3 -0
  18. package/dist/lib/getTaliwareCredentials.js +26 -0
  19. package/dist/lib/getTaliwareCredentials.js.map +1 -0
  20. package/dist/lib/initiateMint.d.ts +16 -0
  21. package/dist/lib/initiateMint.js +25 -0
  22. package/dist/lib/initiateMint.js.map +1 -0
  23. package/dist/lib/mapOutput.d.ts +3 -0
  24. package/dist/lib/mapOutput.js +69 -0
  25. package/dist/lib/mapOutput.js.map +1 -0
  26. package/dist/lib/pollNftStatus.d.ts +12 -0
  27. package/dist/lib/pollNftStatus.js +87 -0
  28. package/dist/lib/pollNftStatus.js.map +1 -0
  29. package/dist/lib/resolveSmartContractId.d.ts +2 -0
  30. package/dist/lib/resolveSmartContractId.js +16 -0
  31. package/dist/lib/resolveSmartContractId.js.map +1 -0
  32. package/dist/lib/resolveUserId.d.ts +3 -0
  33. package/dist/lib/resolveUserId.js +40 -0
  34. package/dist/lib/resolveUserId.js.map +1 -0
  35. package/dist/lib/toolParameters.d.ts +3 -0
  36. package/dist/lib/toolParameters.js +115 -0
  37. package/dist/lib/toolParameters.js.map +1 -0
  38. package/dist/lib/types.d.ts +88 -0
  39. package/dist/lib/types.js +19 -0
  40. package/dist/lib/types.js.map +1 -0
  41. package/dist/lib/validateMintInputs.d.ts +6 -0
  42. package/dist/lib/validateMintInputs.js +100 -0
  43. package/dist/lib/validateMintInputs.js.map +1 -0
  44. package/dist/nodes/TaliwareMint/TaliwareMint.node.d.ts +5 -0
  45. package/dist/nodes/TaliwareMint/TaliwareMint.node.js +101 -0
  46. package/dist/nodes/TaliwareMint/TaliwareMint.node.js.map +1 -0
  47. package/dist/nodes/TaliwareMint/TaliwareMint.node.json +21 -0
  48. package/dist/nodes/TaliwareMint/taliware.dark.svg +13 -0
  49. package/dist/nodes/TaliwareMint/taliware.svg +13 -0
  50. package/dist/package.json +59 -0
  51. package/dist/tsconfig.tsbuildinfo +1 -0
  52. package/package.json +59 -0
  53. package/workflows/README.md +33 -0
  54. package/workflows/agent-tool-mint.example.json +144 -0
  55. package/workflows/manual-mint-binary.example.json +83 -0
  56. package/workflows/manual-mint-testnet.example.json +60 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TALIWARE
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,291 @@
1
+ # n8n-nodes-taliware
2
+
3
+ TALIWARE CL2C biometric NFT mint node for [n8n](https://n8n.io/) — manually execute mints or expose **Mint Biometric NFT** as an AI Agent tool.
4
+
5
+ The node is a thin gate to the [CL2C API](https://developer.taliware.com/api/cl2c.yaml): resolve owner → create NFT → poll until `MINTED` or `FAILED` → return agent-readable JSON. Confida biometric approval happens on the owner's mobile device after create; this package does not embed the Confida SDK.
6
+
7
+ **Requirements:** n8n **≥ 1.79.0** (community AI Agent tools).
8
+
9
+ ---
10
+
11
+ ## Quick Start
12
+
13
+ Goal: first successful testnet mint in under 30 minutes.
14
+
15
+ ### 1. Install the community node
16
+
17
+ On your n8n host, set environment variables:
18
+
19
+ ```bash
20
+ N8N_COMMUNITY_PACKAGES_ENABLED=true
21
+ N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true # required for AI Agent tools
22
+ ```
23
+
24
+ Restart n8n, then: **Settings → Community Nodes → Install** → enter `n8n-nodes-taliware`.
25
+
26
+ ### 2. Create Taliware API credentials
27
+
28
+ In n8n: **Credentials → New → Taliware API**
29
+
30
+ | Field | Required | Notes |
31
+ |-------|----------|-------|
32
+ | API Key | Yes | Sent as `x-api-key` on CL2C requests only — never logged |
33
+ | Base URL | No | Default: `https://developer.taliware.com/api/v1` |
34
+ | Default Smart Contract ID | No | UUID of a contract with `env: TESTNET` for testnet mints |
35
+
36
+ **Testnet:** Use the same API host. Pick a smart contract where `env` is `TESTNET` (via [CL2C API](https://developer.taliware.com/api/cl2c.yaml) or TALIWARE platform). Set that UUID in credentials or override it on the node.
37
+
38
+ List contracts (example):
39
+
40
+ ```bash
41
+ curl -s -H "x-api-key: YOUR_KEY" \
42
+ "https://developer.taliware.com/api/v1/cl2c/contract?limit=50" | jq '.data[] | {id, name, env, network}'
43
+ ```
44
+
45
+ ### 3. Invite the NFT owner
46
+
47
+ The owner must exist on the TALIWARE platform before mint. Either:
48
+
49
+ - **Platform UI** — invite the user via the TALIWARE dashboard, or
50
+ - **API** — `POST /cl2c/user` with the owner's email:
51
+
52
+ ```bash
53
+ curl -s -X POST "https://developer.taliware.com/api/v1/cl2c/user" \
54
+ -H "x-api-key: YOUR_KEY" \
55
+ -H "Content-Type: application/json" \
56
+ -d '{"email":"owner@example.com"}'
57
+ ```
58
+
59
+ The owner must complete Confida setup (see below) before mint can succeed.
60
+
61
+ **Owner User ID:** When resolving by email, the node uses the org entry `id` from `GET /cl2c/user` (UUID), **not** the nested `user.id` platform identifier. You can paste that UUID into **Owner User ID** to skip email lookup:
62
+
63
+ ```bash
64
+ curl -s -H "x-api-key: YOUR_KEY" \
65
+ "https://developer.taliware.com/api/v1/cl2c/user?search=owner@example.com" | jq '.data[] | {id, email, status}'
66
+ ```
67
+
68
+ Use the top-level `id` field in the mint request.
69
+
70
+ ### 4. Run your first manual mint
71
+
72
+ 1. Add **Mint Biometric NFT** to a workflow.
73
+ 2. Select **Taliware API** credentials.
74
+ 3. Set parameters:
75
+ - **Owner Email** (or **Owner User ID**)
76
+ - **Name** (required)
77
+ - **Image Path** — HTTPS URL, base64 (`data:image/...;base64,...` or raw), or leave empty and pass binary on the input item (see **Binary Property Name**)
78
+ - **Smart Contract ID** — leave empty to use credential default, or paste a TESTNET contract UUID
79
+ 4. Click **Execute workflow**.
80
+ 5. The owner receives a Confida mobile notification — complete fingerprint approval on device.
81
+ 6. The node polls until `status` is `MINTED` or `FAILED` (default: 10 s interval, 20 min timeout).
82
+
83
+ On success, output includes `status: "MINTED"`, `mintedAt`, and `smartContract.contractAddress`.
84
+
85
+ **Mint from a local file:** Import [`workflows/manual-mint-binary.example.json`](workflows/manual-mint-binary.example.json) — **Read Binary File** (set `filePath` to your PNG/JPEG) → **Mint Biometric NFT** with **Image Path** empty and **Binary Property Name** `data`.
86
+
87
+ If Confida confirms but the node ends with `status: FAILED` and `mintedAt: null`, the create + IPFS + biometric steps succeeded; on-chain mint likely failed (e.g. unfunded org wallet / CRDB tokens). Retry after TALIWARE recharges testnet funds — no node change required.
88
+
89
+ ---
90
+
91
+ ## Confida (biometric approval)
92
+
93
+ TALIWARE orchestrates [Confida](https://developer.taliware.com/docs/confida) for biometric identity verification. This node **does not** embed the Confida SDK or capture biometrics inside n8n.
94
+
95
+ What happens:
96
+
97
+ 1. Owner is invited to the platform and completes Confida setup on mobile.
98
+ 2. `POST /cl2c/nft` creates the NFT with `status: CREATED` and triggers Confida notifications.
99
+ 3. Owner approves on mobile (fingerprint).
100
+ 4. This node polls `GET /cl2c/nft/{id}` until terminal status.
101
+
102
+ If the owner ignores the notification, polling hits the timeout — see **ConfidaTimeout** in [Error Reference](#error-reference).
103
+
104
+ Full Confida documentation: [developer.taliware.com/docs/confida](https://developer.taliware.com/docs/confida)
105
+
106
+ ---
107
+
108
+ ## AI Agent tool
109
+
110
+ ### Connect the tool
111
+
112
+ 1. Confirm env vars from [Quick Start](#1-install-the-community-node) are set.
113
+ 2. Add an **AI Agent** node to your workflow.
114
+ 3. On the agent's **Tools** input, add **Mint Biometric NFT Tool** (the wrapped variant of this node).
115
+ 4. Select credentials and configure poll/debug settings on the tool node if needed.
116
+
117
+ Agent-supplied parameters (via `$fromAI` when the tool is connected):
118
+
119
+ | Parameter | Required | Notes |
120
+ |-----------|----------|-------|
121
+ | `name` | Yes | NFT display name |
122
+ | `description` | No | Photo or artwork description |
123
+ | `imagePath` | No | HTTPS URL or base64 image for `MintRequest.imagePath` |
124
+ | `ownerEmail` | One of email/userId | Platform user email |
125
+ | `ownerUserId` | One of email/userId | Org entry `id` from `GET /cl2c/user`; skips email lookup when set |
126
+ | `smartContractId` | No | Overrides credential default |
127
+ | `ownershipPercentage` | No | Default 100 |
128
+
129
+ Manual **Execute** and **Tool** invoke share the same `execute()` code path.
130
+
131
+ ### Tool output fields (for agent prompts)
132
+
133
+ On `MINTED`, the tool returns a single JSON item (under ~8 KB, no raw image bytes):
134
+
135
+ ```json
136
+ {
137
+ "id": "uuid",
138
+ "status": "MINTED",
139
+ "name": "My Photo NFT",
140
+ "description": "…",
141
+ "imagePath": "https://…",
142
+ "externalUrl": "https://…",
143
+ "mintedAt": "2026-05-30T12:00:00.000Z",
144
+ "smartContract": {
145
+ "id": "uuid",
146
+ "contractAddress": "0x…",
147
+ "env": "TESTNET",
148
+ "network": "cordoba"
149
+ },
150
+ "copyright": [
151
+ {
152
+ "email": "owner@example.com",
153
+ "status": "…",
154
+ "percentage": 100,
155
+ "walletAddress": "0x…"
156
+ }
157
+ ]
158
+ }
159
+ ```
160
+
161
+ Use `id`, `status`, `mintedAt`, `smartContract.contractAddress`, and `copyright[]` in agent system prompts so the model can confirm mint success to the user.
162
+
163
+ ### Workflow Tool workaround (n8n #26202)
164
+
165
+ Some n8n versions have a [FIFO queue ordering bug](https://github.com/n8n-io/n8n/issues/26202) where AI Agents receive an **empty observation** from `usableAsTool` community nodes even though the tool executed successfully.
166
+
167
+ **Symptom:** Agent says the tool returned nothing; execution log shows the mint node succeeded with JSON output.
168
+
169
+ **Workaround:** Wrap the mint in a sub-workflow and connect it via n8n's native **Workflow Tool** (uses `supplyData`, not the `usableAsTool` wrapper):
170
+
171
+ 1. Create a workflow with **Execute Workflow Trigger** (or **When Executed by Another Workflow**) → **Mint Biometric NFT** → return mapped JSON.
172
+ 2. In your agent workflow, add **Workflow Tool** pointing at that sub-workflow instead of **Mint Biometric NFT Tool** directly.
173
+ 3. Map tool inputs in the sub-workflow trigger to mint node parameters.
174
+
175
+ Upgrade n8n when a fix for #26202 is available in your release channel.
176
+
177
+ ---
178
+
179
+ ## Error Reference
180
+
181
+ All failures use `NodeOperationError` with a prefix in the execution log:
182
+
183
+ | Prefix | Likely cause | Remediation |
184
+ |--------|--------------|-------------|
185
+ | **Configuration:** | Invalid or missing API key, bad base URL, missing default smart contract when node override is empty | Verify **Taliware API** credentials; set **Default Smart Contract ID** or node **Smart Contract ID** |
186
+ | **Validation:** | Missing name, missing owner email/userId, owner not found, invalid `imagePath`, CL2C 400/404 validation | Fix parameters before retry; invite owner via platform or `POST /cl2c/user` |
187
+ | **API:** | CL2C request failed, NFT `status: FAILED` after poll | Read message and API `details`; check contract env, owner Confida status, image data/URL |
188
+ | **ConfidaTimeout:** | Poll timeout — owner did not complete biometric approval in time | Ask owner to approve on mobile; increase **Poll Timeout Minutes**; node does **not** auto re-mint |
189
+ | **n8nTool:** | n8n tool-layer issues (e.g. empty observation from #26202) | Use [Workflow Tool workaround](#workflow-tool-workaround-n8n-26202); upgrade n8n |
190
+
191
+ Example messages:
192
+
193
+ - `Validation: Name is required`
194
+ - `Validation: Owner not found — user must be invited via TALIWARE platform or POST /cl2c/user`
195
+ - `Validation: imagePath must be an HTTPS URL, a data:image/...;base64,... URI, or raw base64 image data`
196
+ - `ConfidaTimeout: Mint approval timed out — Confida biometric approval may still be pending (id=…, status=CREATED)`
197
+
198
+ ---
199
+
200
+ ## Reliability
201
+
202
+ ### Duplicate mint on agent retries
203
+
204
+ CL2C v1.0.0 has **no idempotency key**. If an AI Agent retries a failed or timed-out tool call, each attempt creates a **new** NFT via `POST /cl2c/nft`. Design agent prompts and error handling to avoid blind retries after partial success (e.g. after create but before poll completes).
205
+
206
+ ### HTTP 429 rate limits
207
+
208
+ The CL2C client retries up to 3 times with exponential backoff (1 s, 2 s, 4 s) on HTTP 429.
209
+
210
+ ### Poll behaviour
211
+
212
+ The node polls until NFT-level `status` is `MINTED` or `FAILED`. Success is determined by `status === 'MINTED'`, not individual `copyright[].status` values. Defaults: 10 s interval, 20 min timeout (configurable on the node).
213
+
214
+ ### Debug mode
215
+
216
+ Enable **Debug** on the node to log NFT `id`, poll count, and last `status` only — never API keys or full email addresses.
217
+
218
+ ---
219
+
220
+ ## Publishing
221
+
222
+ Version **0.1.0** is published to npm as [`n8n-nodes-taliware`](https://www.npmjs.com/package/n8n-nodes-taliware).
223
+
224
+ ### Local checks before release
225
+
226
+ ```bash
227
+ npm run lint
228
+ npm run build
229
+ npm pack --dry-run # verify tarball: dist/, workflows/, README, LICENSE, CHANGELOG only
230
+ ```
231
+
232
+ The package `files` field includes only `dist/` and `workflows/` — no BMad or planning artifacts are shipped.
233
+
234
+ ### Release to npm (with provenance)
235
+
236
+ 1. Configure [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers) (OIDC) on npmjs.com for this repo's `publish.yml` workflow, or set `NPM_TOKEN` in GitHub secrets.
237
+ 2. Locally bump and tag (does **not** publish from your machine):
238
+
239
+ ```bash
240
+ npm run release
241
+ ```
242
+
243
+ 3. Push the tag (e.g. `0.1.0`) — GitHub Actions runs `.github/workflows/publish.yml`, which lint/builds and runs `npm publish` with `NPM_CONFIG_PROVENANCE=true`.
244
+
245
+ Example workflows for post-publish validation are in [`workflows/`](workflows/).
246
+
247
+ ---
248
+
249
+ ## Development
250
+
251
+ **Requirements:** Node.js ≥ 22.16 (n8n engine), npm. For local `npm run dev` with bundled n8n, **Node 22 LTS** is recommended — Node 24 may fail on native `isolated-vm` builds.
252
+
253
+ ```bash
254
+ cd n8n-nodes-taliware
255
+ npm install # uses .npmrc ignore-scripts=true for @n8n/node-cli
256
+ npm run build
257
+ npm run lint
258
+ npm run dev # symlinks to ~/.n8n-node-cli and starts n8n + tsc watch
259
+ ```
260
+
261
+ ### Local n8n test (while testnet tokens recharge)
262
+
263
+ 1. `npm run build && npm run dev` (or `docker compose up` after build).
264
+ 2. Open http://localhost:5678 → **Credentials → Taliware API**:
265
+ - **API Key:** your `n8nplugin…` CL2C key (`x-api-key`, not Bearer)
266
+ - **Default Smart Contract ID:** TESTNET contract UUID
267
+ 3. Import `workflows/manual-mint-binary.example.json`.
268
+ 4. Set **Read Binary File → filePath** to a local image (e.g. `scripts/fixtures/testest.png` during dev).
269
+ 5. Set **Owner Email** to a Confida-ready ACTIVE user; leave **Image Path** empty.
270
+ 6. Execute → approve Confida on mobile → node polls until `MINTED` or `FAILED`.
271
+
272
+ See [`scripts/README.md`](scripts/README.md) for curl smoke tests without n8n.
273
+
274
+ **External n8n:**
275
+
276
+ ```bash
277
+ # Terminal 1
278
+ npm run dev -- --external-n8n
279
+
280
+ # Terminal 2
281
+ N8N_DEV_RELOAD=true N8N_USER_FOLDER=$HOME/.n8n-node-cli n8n start
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Resources
287
+
288
+ - [CL2C OpenAPI spec (cl2c.yaml)](https://developer.taliware.com/api/cl2c.yaml)
289
+ - [Confida documentation](https://developer.taliware.com/docs/confida)
290
+ - [n8n community nodes](https://docs.n8n.io/integrations/community-nodes/)
291
+ - [n8n $fromAI() function](https://docs.n8n.io/advanced-ai/examples/using-the-fromai-function/)
@@ -0,0 +1,9 @@
1
+ import type { ICredentialTestRequest, ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
2
+ export declare class TaliwareApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ icon: Icon;
6
+ documentationUrl: string;
7
+ properties: INodeProperties[];
8
+ test: ICredentialTestRequest;
9
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaliwareApi = void 0;
4
+ const DEFAULT_BASE_URL = 'https://developer.taliware.com/api/v1';
5
+ class TaliwareApi {
6
+ constructor() {
7
+ this.name = 'taliwareApi';
8
+ this.displayName = 'Taliware API';
9
+ this.icon = {
10
+ light: 'file:icons/taliware.svg',
11
+ dark: 'file:icons/taliware.dark.svg',
12
+ };
13
+ this.documentationUrl = 'https://developer.taliware.com/docs/api';
14
+ this.properties = [
15
+ {
16
+ displayName: 'API Key',
17
+ name: 'apiKey',
18
+ type: 'string',
19
+ typeOptions: { password: true },
20
+ required: true,
21
+ default: '',
22
+ description: 'Organization API key from the TALIWARE Developer Platform',
23
+ },
24
+ {
25
+ displayName: 'Base URL',
26
+ name: 'baseUrl',
27
+ type: 'string',
28
+ default: DEFAULT_BASE_URL,
29
+ description: 'CL2C API base URL (testnet uses the same host; select a TESTNET contract id)',
30
+ },
31
+ {
32
+ displayName: 'Default Smart Contract ID',
33
+ name: 'defaultSmartContractId',
34
+ type: 'string',
35
+ default: '',
36
+ description: 'Optional default contract UUID for mint operations',
37
+ },
38
+ ];
39
+ this.test = {
40
+ request: {
41
+ baseURL: '={{$credentials.baseUrl}}',
42
+ url: '/cl2c/user',
43
+ qs: { limit: '1' },
44
+ headers: {
45
+ 'x-api-key': '={{$credentials.apiKey}}',
46
+ },
47
+ },
48
+ };
49
+ }
50
+ }
51
+ exports.TaliwareApi = TaliwareApi;
52
+ //# sourceMappingURL=TaliwareApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TaliwareApi.credentials.js","sourceRoot":"","sources":["../../credentials/TaliwareApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAM,gBAAgB,GAAG,uCAAuC,CAAC;AAEjE,MAAa,WAAW;IAAxB;QACC,SAAI,GAAG,aAAa,CAAC;QAErB,gBAAW,GAAG,cAAc,CAAC;QAE7B,SAAI,GAAS;YACZ,KAAK,EAAE,yBAAyB;YAChC,IAAI,EAAE,8BAA8B;SACpC,CAAC;QAEF,qBAAgB,GAAG,yCAAyC,CAAC;QAE7D,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,QAAQ,EAAE,IAAI;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,2DAA2D;aACxE;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,gBAAgB;gBACzB,WAAW,EAAE,8EAA8E;aAC3F;YACD;gBACC,WAAW,EAAE,2BAA2B;gBACxC,IAAI,EAAE,wBAAwB;gBAC9B,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,oDAAoD;aACjE;SACD,CAAC;QAEF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,YAAY;gBACjB,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBAClB,OAAO,EAAE;oBACR,WAAW,EAAE,0BAA0B;iBACvC;aACD;SACD,CAAC;IACH,CAAC;CAAA;AAhDD,kCAgDC"}
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="aquamarine"
2
+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cpu">
3
+ <rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
4
+ <rect x="9" y="9" width="6" height="6"></rect>
5
+ <line x1="9" y1="1" x2="9" y2="4"></line>
6
+ <line x1="15" y1="1" x2="15" y2="4"></line>
7
+ <line x1="9" y1="20" x2="9" y2="23"></line>
8
+ <line x1="15" y1="20" x2="15" y2="23"></line>
9
+ <line x1="20" y1="9" x2="23" y2="9"></line>
10
+ <line x1="20" y1="14" x2="23" y2="14"></line>
11
+ <line x1="1" y1="9" x2="4" y2="9"></line>
12
+ <line x1="1" y1="14" x2="4" y2="14"></line>
13
+ </svg>
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="darkblue"
2
+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cpu">
3
+ <rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
4
+ <rect x="9" y="9" width="6" height="6"></rect>
5
+ <line x1="9" y1="1" x2="9" y2="4"></line>
6
+ <line x1="15" y1="1" x2="15" y2="4"></line>
7
+ <line x1="9" y1="20" x2="9" y2="23"></line>
8
+ <line x1="15" y1="20" x2="15" y2="23"></line>
9
+ <line x1="20" y1="9" x2="23" y2="9"></line>
10
+ <line x1="20" y1="14" x2="23" y2="14"></line>
11
+ <line x1="1" y1="9" x2="4" y2="9"></line>
12
+ <line x1="1" y1="14" x2="4" y2="14"></line>
13
+ </svg>
@@ -0,0 +1,18 @@
1
+ import type { INode } from 'n8n-workflow';
2
+ import type { TaliwareApiCredentials } from './types';
3
+ export interface Cl2cRequestOptions {
4
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
5
+ path: string;
6
+ query?: Record<string, string | number | undefined>;
7
+ body?: unknown;
8
+ }
9
+ export declare class Cl2cClient {
10
+ private readonly credentials;
11
+ private readonly node;
12
+ constructor(credentials: TaliwareApiCredentials, node: INode);
13
+ request<T>(options: Cl2cRequestOptions): Promise<T>;
14
+ private buildUrl;
15
+ private parseResponse;
16
+ private assertCl2cPath;
17
+ }
18
+ export declare function createCl2cClient(credentials: TaliwareApiCredentials, node: INode): Cl2cClient;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Cl2cClient = void 0;
4
+ exports.createCl2cClient = createCl2cClient;
5
+ const n8n_workflow_1 = require("n8n-workflow");
6
+ const errors_1 = require("./errors");
7
+ const MAX_429_RETRIES = 3;
8
+ const INITIAL_BACKOFF_MS = 1000;
9
+ class Cl2cClient {
10
+ constructor(credentials, node) {
11
+ this.credentials = credentials;
12
+ this.node = node;
13
+ }
14
+ async request(options) {
15
+ this.assertCl2cPath(options.path);
16
+ for (let attempt = 0; attempt <= MAX_429_RETRIES; attempt++) {
17
+ const response = await fetch(this.buildUrl(options), {
18
+ method: options.method,
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ 'x-api-key': this.credentials.apiKey,
22
+ },
23
+ body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
24
+ });
25
+ if (response.status === 429 && attempt < MAX_429_RETRIES) {
26
+ await (0, n8n_workflow_1.sleep)(INITIAL_BACKOFF_MS * Math.pow(2, attempt));
27
+ continue;
28
+ }
29
+ return await this.parseResponse(response);
30
+ }
31
+ throw (0, errors_1.configurationError)(this.node, 'Exceeded maximum retries for rate-limited CL2C request');
32
+ }
33
+ buildUrl(options) {
34
+ const url = new URL(options.path.startsWith('/') ? options.path.slice(1) : options.path, `${this.credentials.baseUrl}/`);
35
+ if (options.query) {
36
+ for (const [key, value] of Object.entries(options.query)) {
37
+ if (value !== undefined && value !== '') {
38
+ url.searchParams.set(key, String(value));
39
+ }
40
+ }
41
+ }
42
+ return url.toString();
43
+ }
44
+ async parseResponse(response) {
45
+ if (response.status === 401) {
46
+ throw (0, errors_1.configurationError)(this.node, errors_1.INVALID_API_KEY_MESSAGE);
47
+ }
48
+ const payload = await readResponseBody(response);
49
+ if (!response.ok) {
50
+ throw (0, errors_1.httpErrorFromResponse)(this.node, response.status, payload, `HTTP ${response.status}: CL2C request failed`);
51
+ }
52
+ return payload;
53
+ }
54
+ assertCl2cPath(path) {
55
+ const normalized = path.startsWith('/') ? path : `/${path}`;
56
+ if (!normalized.startsWith('/cl2c/')) {
57
+ throw (0, errors_1.configurationError)(this.node, `Invalid CL2C path: ${normalized}`);
58
+ }
59
+ }
60
+ }
61
+ exports.Cl2cClient = Cl2cClient;
62
+ function createCl2cClient(credentials, node) {
63
+ return new Cl2cClient(credentials, node);
64
+ }
65
+ async function readResponseBody(response) {
66
+ const text = await response.text();
67
+ if (!text) {
68
+ return undefined;
69
+ }
70
+ try {
71
+ return JSON.parse(text);
72
+ }
73
+ catch {
74
+ return text;
75
+ }
76
+ }
77
+ //# sourceMappingURL=cl2cClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cl2cClient.js","sourceRoot":"","sources":["../../lib/cl2cClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Cl2cClient } from './cl2cClient';
2
+ import { type NftDetail } from './types';
3
+ export interface CreateNftInput {
4
+ name: string;
5
+ smartContractId: string;
6
+ userId: string;
7
+ ownershipPercentage: number;
8
+ description?: string;
9
+ imagePath?: string;
10
+ externalUrl?: string;
11
+ }
12
+ export declare function createNft(client: Cl2cClient, input: CreateNftInput): Promise<NftDetail>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNft = createNft;
4
+ const types_1 = require("./types");
5
+ async function createNft(client, input) {
6
+ var _a, _b, _c;
7
+ const body = {
8
+ name: input.name.trim(),
9
+ smartContractId: input.smartContractId,
10
+ supply: 1,
11
+ copyright: [
12
+ {
13
+ userId: input.userId,
14
+ percentage: input.ownershipPercentage,
15
+ },
16
+ ],
17
+ };
18
+ const description = (_a = input.description) === null || _a === void 0 ? void 0 : _a.trim();
19
+ if (description) {
20
+ body.description = description;
21
+ }
22
+ const imagePath = (_b = input.imagePath) === null || _b === void 0 ? void 0 : _b.trim();
23
+ if (imagePath) {
24
+ body.imagePath = imagePath;
25
+ }
26
+ const externalUrl = (_c = input.externalUrl) === null || _c === void 0 ? void 0 : _c.trim();
27
+ if (externalUrl) {
28
+ body.externalUrl = externalUrl;
29
+ }
30
+ return await client.request({
31
+ method: 'POST',
32
+ path: types_1.CL2C_PATHS.nft,
33
+ body,
34
+ });
35
+ }
36
+ //# sourceMappingURL=createNft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createNft.js","sourceRoot":"","sources":["../../lib/createNft.ts"],"names":[],"mappings":";;AAaA,8BAoCC;AAhDD,mCAAuE;AAYhE,KAAK,UAAU,SAAS,CAC9B,MAAkB,EAClB,KAAqB;;IAErB,MAAM,IAAI,GAAgB;QACzB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;QACvB,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,MAAM,EAAE,CAAC;QACT,SAAS,EAAE;YACV;gBACC,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,UAAU,EAAE,KAAK,CAAC,mBAAmB;aACrC;SACD;KACD,CAAC;IAEF,MAAM,WAAW,GAAG,MAAA,KAAK,CAAC,WAAW,0CAAE,IAAI,EAAE,CAAC;IAC9C,IAAI,WAAW,EAAE,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IAChC,CAAC;IAED,MAAM,SAAS,GAAG,MAAA,KAAK,CAAC,SAAS,0CAAE,IAAI,EAAE,CAAC;IAC1C,IAAI,SAAS,EAAE,CAAC;QACf,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC5B,CAAC;IAED,MAAM,WAAW,GAAG,MAAA,KAAK,CAAC,WAAW,0CAAE,IAAI,EAAE,CAAC;IAC9C,IAAI,WAAW,EAAE,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IAChC,CAAC;IAED,OAAO,MAAM,MAAM,CAAC,OAAO,CAAY;QACtC,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,kBAAU,CAAC,GAAG;QACpB,IAAI;KACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { INode } from 'n8n-workflow';
2
+ import { NodeOperationError } from 'n8n-workflow';
3
+ import type { Cl2cErrorResponse } from './types';
4
+ export type ErrorPrefix = 'Configuration' | 'Validation' | 'API' | 'ConfidaTimeout' | 'n8nTool';
5
+ export declare function prefixedError(prefix: ErrorPrefix, message: string): string;
6
+ export declare function createNodeError(node: INode, prefix: ErrorPrefix, message: string): NodeOperationError;
7
+ export declare function configurationError(node: INode, message: string): NodeOperationError;
8
+ export declare function validationError(node: INode, message: string): NodeOperationError;
9
+ export declare function apiError(node: INode, message: string): NodeOperationError;
10
+ export declare function confidaTimeoutError(node: INode, message: string): NodeOperationError;
11
+ export declare function n8nToolError(node: INode, message: string): NodeOperationError;
12
+ export declare const INVALID_API_KEY_MESSAGE = "Invalid API key or unauthorized access";
13
+ export declare function formatCl2cApiErrorMessage(payload: unknown, fallback?: string): string;
14
+ export declare function apiErrorFromPayload(node: INode, payload: unknown, fallback?: string): NodeOperationError;
15
+ export declare function httpErrorFromResponse(node: INode, status: number, payload: unknown, fallback?: string): NodeOperationError;
16
+ export declare function isCl2cErrorResponse(payload: unknown): payload is Cl2cErrorResponse;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.INVALID_API_KEY_MESSAGE = void 0;
4
+ exports.prefixedError = prefixedError;
5
+ exports.createNodeError = createNodeError;
6
+ exports.configurationError = configurationError;
7
+ exports.validationError = validationError;
8
+ exports.apiError = apiError;
9
+ exports.confidaTimeoutError = confidaTimeoutError;
10
+ exports.n8nToolError = n8nToolError;
11
+ exports.formatCl2cApiErrorMessage = formatCl2cApiErrorMessage;
12
+ exports.apiErrorFromPayload = apiErrorFromPayload;
13
+ exports.httpErrorFromResponse = httpErrorFromResponse;
14
+ exports.isCl2cErrorResponse = isCl2cErrorResponse;
15
+ const n8n_workflow_1 = require("n8n-workflow");
16
+ function prefixedError(prefix, message) {
17
+ return `${prefix}: ${message}`;
18
+ }
19
+ function createNodeError(node, prefix, message) {
20
+ return new n8n_workflow_1.NodeOperationError(node, prefixedError(prefix, message));
21
+ }
22
+ function configurationError(node, message) {
23
+ return createNodeError(node, 'Configuration', message);
24
+ }
25
+ function validationError(node, message) {
26
+ return createNodeError(node, 'Validation', message);
27
+ }
28
+ function apiError(node, message) {
29
+ return createNodeError(node, 'API', message);
30
+ }
31
+ function confidaTimeoutError(node, message) {
32
+ return createNodeError(node, 'ConfidaTimeout', message);
33
+ }
34
+ function n8nToolError(node, message) {
35
+ return createNodeError(node, 'n8nTool', message);
36
+ }
37
+ exports.INVALID_API_KEY_MESSAGE = 'Invalid API key or unauthorized access';
38
+ function formatCl2cApiErrorMessage(payload, fallback = 'API request failed') {
39
+ if (!payload || typeof payload !== 'object') {
40
+ return fallback;
41
+ }
42
+ const record = payload;
43
+ const parts = [];
44
+ if (typeof record.error === 'string' && record.error.trim()) {
45
+ parts.push(record.error.trim());
46
+ }
47
+ if (Array.isArray(record.details)) {
48
+ const detailMessages = record.details.filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
49
+ if (detailMessages.length > 0) {
50
+ parts.push(detailMessages.join('; '));
51
+ }
52
+ }
53
+ return parts.length > 0 ? parts.join(' — ') : fallback;
54
+ }
55
+ function apiErrorFromPayload(node, payload, fallback) {
56
+ return apiError(node, formatCl2cApiErrorMessage(payload, fallback));
57
+ }
58
+ function httpErrorFromResponse(node, status, payload, fallback) {
59
+ const message = formatCl2cApiErrorMessage(payload, fallback !== null && fallback !== void 0 ? fallback : `HTTP ${status}: CL2C request failed`);
60
+ if (status === 400 || status === 404) {
61
+ return validationError(node, message);
62
+ }
63
+ return apiError(node, message);
64
+ }
65
+ function isCl2cErrorResponse(payload) {
66
+ return Boolean(payload && typeof payload === 'object' && 'error' in payload);
67
+ }
68
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../lib/errors.ts"],"names":[],"mappings":"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"}