n8n-nodes-caedral 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # n8n-nodes-caedral
2
+
3
+ Official [n8n](https://n8n.io) community node for the [Caedral API](https://caedral.com). Send chat completions and check prepaid API usage from your workflows.
4
+
5
+ ## Installation
6
+
7
+ ### npm (recommended for self-hosted n8n)
8
+
9
+ ```bash
10
+ cd ~/.n8n/custom
11
+ npm install n8n-nodes-caedral
12
+ # Restart n8n
13
+ ```
14
+
15
+ ### Local development (monorepo)
16
+
17
+ ```bash
18
+ cd n8n-nodes-caedral
19
+ npm install && npm run build
20
+ npm link
21
+ cd ~/.n8n/custom && npm link n8n-nodes-caedral
22
+ ```
23
+
24
+ ## Credentials
25
+
26
+ Create **Caedral API** credentials in n8n:
27
+
28
+ | Field | Description |
29
+ |-------|-------------|
30
+ | API Key | Your `cd_live_...` key from [Dashboard → API Keys](https://caedral.com/dashboard/api-keys) |
31
+ | Base URL | `https://api.caedral.com` (production) or `http://localhost:5001` (local gateway) |
32
+
33
+ Connection test calls `GET /v1/usage`.
34
+
35
+ ## Operations
36
+
37
+ ### Chat Completion
38
+
39
+ Send a chat completion to any Caedral model tier (`caedral-base`, `caedral-titan`, `caedral-olympus`, `caedral-primordial`).
40
+
41
+ Configure:
42
+
43
+ - **Model** — dropdown of available tiers
44
+ - **Messages** — JSON array or individual role/content fields
45
+ - **Temperature**, **Max Tokens** — optional generation parameters
46
+
47
+ Returns parsed `content`, `model`, `finishReason`, and `usage` fields.
48
+
49
+ ### Get Usage
50
+
51
+ Returns prepaid API balance, weekly pool (for Caedral Chat), plan info, and account status via `GET /v1/usage`.
52
+
53
+ ## Example workflow
54
+
55
+ 1. **Get Usage** — check `balanceCents` before a paid-tier call
56
+ 2. **IF** — continue only if balance is sufficient
57
+ 3. **Chat Completion** — run inference with `caedral-olympus`
58
+ 4. Send output to Slack, email, or downstream nodes
59
+
60
+ See [Example n8n workflow](https://caedral.com/docs/n8n-example) in the Caedral docs.
61
+
62
+ ## HTTP details
63
+
64
+ The node calls the Caedral API gateway directly:
65
+
66
+ - `POST /v1/chat/completions` — chat completion
67
+ - `GET /v1/usage` — usage summary
68
+
69
+ Authentication: `Authorization: Bearer <apiKey>`.
70
+
71
+ ## Development
72
+
73
+ ```bash
74
+ npm install
75
+ npm run build
76
+ npm test
77
+ ```
78
+
79
+ Integration tests require a running local gateway on port **5001** and `DATABASE_URL` in the repo root `.env`.
80
+
81
+ ## Publishing
82
+
83
+ See [PUBLISHING.md](./PUBLISHING.md) for npm publish and n8n Creator Portal submission steps.
84
+
85
+ ## Links
86
+
87
+ - [Caedral docs — n8n overview](https://caedral.com/docs/n8n-overview)
88
+ - [npm package](https://www.npmjs.com/package/n8n-nodes-caedral)
89
+ - [GitHub](https://github.com/caedral/n8n-nodes-caedral)
90
+
91
+ ## License
92
+
93
+ MIT
@@ -0,0 +1,20 @@
1
+ import type { ICredentialTestRequest, ICredentialType, INodeProperties } from "n8n-workflow";
2
+ /**
3
+ * n8n credential type for the Caedral API.
4
+ *
5
+ * Collects the user's `apiKey` (stored securely as a password
6
+ * field) and an optional `baseUrl` override for self-hosted or
7
+ * local deployments. The `test` request validates the credentials
8
+ * by hitting `GET /v1/usage`, which requires a valid API key.
9
+ */
10
+ export declare class CaedralApi implements ICredentialType {
11
+ name: string;
12
+ displayName: string;
13
+ documentationUrl: string;
14
+ icon: {
15
+ readonly light: "file:../../icons/caedral.svg";
16
+ readonly dark: "file:../../icons/caedral.dark.svg";
17
+ };
18
+ properties: INodeProperties[];
19
+ test: ICredentialTestRequest;
20
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CaedralApi = void 0;
4
+ const constants_1 = require("../shared/constants");
5
+ /**
6
+ * n8n credential type for the Caedral API.
7
+ *
8
+ * Collects the user's `apiKey` (stored securely as a password
9
+ * field) and an optional `baseUrl` override for self-hosted or
10
+ * local deployments. The `test` request validates the credentials
11
+ * by hitting `GET /v1/usage`, which requires a valid API key.
12
+ */
13
+ class CaedralApi {
14
+ constructor() {
15
+ this.name = "caedralApi";
16
+ this.displayName = "Caedral API";
17
+ this.documentationUrl = "https://caedral.com/docs";
18
+ this.icon = {
19
+ light: "file:../../icons/caedral.svg",
20
+ dark: "file:../../icons/caedral.dark.svg",
21
+ };
22
+ this.properties = [
23
+ {
24
+ displayName: "API Key",
25
+ name: "apiKey",
26
+ type: "string",
27
+ typeOptions: { password: true },
28
+ default: "",
29
+ required: true,
30
+ placeholder: "cd_live_...",
31
+ description: "Your Caedral API key from the dashboard",
32
+ },
33
+ {
34
+ displayName: "Base URL",
35
+ name: "baseUrl",
36
+ type: "string",
37
+ default: constants_1.DEFAULT_BASE_URL,
38
+ description: "Caedral API gateway URL. Use http://localhost:5001 for local development.",
39
+ },
40
+ ];
41
+ this.test = {
42
+ request: {
43
+ baseURL: "={{$credentials.baseUrl}}",
44
+ url: "/v1/usage",
45
+ method: "GET",
46
+ headers: {
47
+ Authorization: "=Bearer {{$credentials.apiKey}}",
48
+ Accept: "application/json",
49
+ },
50
+ },
51
+ };
52
+ }
53
+ }
54
+ exports.CaedralApi = CaedralApi;
55
+ //# sourceMappingURL=CaedralApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CaedralApi.credentials.js","sourceRoot":"","sources":["../../credentials/CaedralApi.credentials.ts"],"names":[],"mappings":";;;AAMA,mDAAuD;AAEvD;;;;;;;GAOG;AACH,MAAa,UAAU;IAAvB;QACE,SAAI,GAAG,YAAY,CAAC;QAEpB,gBAAW,GAAG,aAAa,CAAC;QAE5B,qBAAgB,GAAG,0BAA0B,CAAC;QAE9C,SAAI,GAAG;YACL,KAAK,EAAE,8BAA8B;YACrC,IAAI,EAAE,mCAAmC;SACjC,CAAC;QAEX,eAAU,GAAsB;YAC9B;gBACE,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,aAAa;gBAC1B,WAAW,EAAE,yCAAyC;aACvD;YACD;gBACE,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,4BAAgB;gBACzB,WAAW,EACT,2EAA2E;aAC9E;SACF,CAAC;QAEF,SAAI,GAA2B;YAC7B,OAAO,EAAE;gBACP,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,WAAW;gBAChB,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE;oBACP,aAAa,EAAE,iCAAiC;oBAChD,MAAM,EAAE,kBAAkB;iBAC3B;aACF;SACF,CAAC;IACJ,CAAC;CAAA;AA5CD,gCA4CC"}
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 100 100" role="img" aria-label="Caedral">
2
+ <g fill="#E8DCC4">
3
+ <polygon points="18,82 40,82 52,42 30,42"/>
4
+ <polygon points="48,82 70,82 86,18 64,18"/>
5
+ </g>
6
+ </svg>
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 100 100" role="img" aria-label="Caedral">
2
+ <g fill="#D4C5A9">
3
+ <polygon points="18,82 40,82 52,42 30,42"/>
4
+ <polygon points="48,82 70,82 86,18 64,18"/>
5
+ </g>
6
+ </svg>
@@ -0,0 +1,36 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
2
+ /**
3
+ * n8n node exposing the Caedral API.
4
+ *
5
+ * Supports two operations:
6
+ * - `chatCompletion` — send a chat completion request to a Caedral
7
+ * model, either with a single simple message or a full JSON
8
+ * messages array.
9
+ * - `getUsage` — fetch the account's current pool, balance, and
10
+ * overage status.
11
+ *
12
+ * Requests are authenticated using the `caedralApi` credentials
13
+ * (API key + optional base URL override).
14
+ */
15
+ export declare class Caedral implements INodeType {
16
+ description: INodeTypeDescription;
17
+ /**
18
+ * Execute the node for every input item.
19
+ *
20
+ * For each item, resolves the selected `operation` and dispatches
21
+ * the corresponding Caedral API call. On success, the response is
22
+ * normalized into a flat JSON object and pushed to the output.
23
+ *
24
+ * When "Continue On Fail" is enabled, per-item errors are captured
25
+ * as `{ error: string }` entries in the output; otherwise the
26
+ * error is re-thrown (wrapped in `NodeOperationError` if it is not
27
+ * already a node error).
28
+ *
29
+ * @returns A single-branch array of output items, one per input.
30
+ * @throws {NodeApiError} If the Caedral API returns a non-2xx
31
+ * status and "Continue On Fail" is disabled.
32
+ * @throws {NodeOperationError} For validation or unknown-operation
33
+ * errors, or when wrapping an unexpected runtime error.
34
+ */
35
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
36
+ }
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Caedral = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const helpers_1 = require("./helpers");
6
+ /**
7
+ * n8n node exposing the Caedral API.
8
+ *
9
+ * Supports two operations:
10
+ * - `chatCompletion` — send a chat completion request to a Caedral
11
+ * model, either with a single simple message or a full JSON
12
+ * messages array.
13
+ * - `getUsage` — fetch the account's current pool, balance, and
14
+ * overage status.
15
+ *
16
+ * Requests are authenticated using the `caedralApi` credentials
17
+ * (API key + optional base URL override).
18
+ */
19
+ class Caedral {
20
+ constructor() {
21
+ this.description = {
22
+ displayName: "Caedral",
23
+ name: "caedral",
24
+ icon: {
25
+ light: "file:../../icons/caedral.svg",
26
+ dark: "file:../../icons/caedral.dark.svg",
27
+ },
28
+ group: ["transform"],
29
+ version: 1,
30
+ subtitle: '={{$parameter["operation"]}}',
31
+ description: "Call Caedral AI models and check usage from your workflows",
32
+ defaults: {
33
+ name: "Caedral",
34
+ },
35
+ inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
36
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
37
+ usableAsTool: true,
38
+ credentials: [
39
+ {
40
+ name: "caedralApi",
41
+ required: true,
42
+ },
43
+ ],
44
+ properties: [
45
+ {
46
+ displayName: "Operation",
47
+ name: "operation",
48
+ type: "options",
49
+ noDataExpression: true,
50
+ options: [
51
+ {
52
+ name: "Chat Completion",
53
+ value: "chatCompletion",
54
+ description: "Send a chat completion request to a Caedral model",
55
+ action: "Send a chat completion",
56
+ },
57
+ {
58
+ name: "Get Usage",
59
+ value: "getUsage",
60
+ description: "Get current pool, balance, and overage status",
61
+ action: "Get usage",
62
+ },
63
+ ],
64
+ default: "chatCompletion",
65
+ },
66
+ {
67
+ displayName: "Model",
68
+ name: "model",
69
+ type: "options",
70
+ displayOptions: {
71
+ show: {
72
+ operation: ["chatCompletion"],
73
+ },
74
+ },
75
+ options: [...helpers_1.MODEL_OPTIONS],
76
+ default: "caedral-base",
77
+ description: "The Caedral model tier to use",
78
+ },
79
+ {
80
+ displayName: "Message Input Mode",
81
+ name: "messageMode",
82
+ type: "options",
83
+ displayOptions: {
84
+ show: {
85
+ operation: ["chatCompletion"],
86
+ },
87
+ },
88
+ options: [
89
+ {
90
+ name: "Simple",
91
+ value: "simple",
92
+ description: "Single user message text",
93
+ },
94
+ {
95
+ name: "JSON",
96
+ value: "json",
97
+ description: "Full messages array as JSON",
98
+ },
99
+ ],
100
+ default: "simple",
101
+ },
102
+ {
103
+ displayName: "Message",
104
+ name: "message",
105
+ type: "string",
106
+ typeOptions: {
107
+ rows: 4,
108
+ },
109
+ displayOptions: {
110
+ show: {
111
+ operation: ["chatCompletion"],
112
+ messageMode: ["simple"],
113
+ },
114
+ },
115
+ default: "",
116
+ placeholder: "Explain quantum computing in one sentence.",
117
+ description: "The user message sent to the model",
118
+ },
119
+ {
120
+ displayName: "Messages JSON",
121
+ name: "messagesJson",
122
+ type: "json",
123
+ displayOptions: {
124
+ show: {
125
+ operation: ["chatCompletion"],
126
+ messageMode: ["json"],
127
+ },
128
+ },
129
+ default: '[{"role":"user","content":"Hello!"}]',
130
+ description: 'Array of message objects, e.g. [{"role":"user","content":"Hello"}]',
131
+ },
132
+ {
133
+ displayName: "Temperature",
134
+ name: "temperature",
135
+ type: "number",
136
+ typeOptions: {
137
+ minValue: 0,
138
+ maxValue: 2,
139
+ numberStepSize: 0.1,
140
+ },
141
+ displayOptions: {
142
+ show: {
143
+ operation: ["chatCompletion"],
144
+ },
145
+ },
146
+ default: 1,
147
+ description: "Sampling temperature (0–2). Leave default to omit from request.",
148
+ },
149
+ {
150
+ displayName: "Max Tokens",
151
+ name: "maxTokens",
152
+ type: "number",
153
+ typeOptions: {
154
+ minValue: 1,
155
+ },
156
+ displayOptions: {
157
+ show: {
158
+ operation: ["chatCompletion"],
159
+ },
160
+ },
161
+ default: 0,
162
+ description: "Maximum tokens to generate. Set to 0 to omit from request.",
163
+ },
164
+ ],
165
+ };
166
+ }
167
+ /**
168
+ * Execute the node for every input item.
169
+ *
170
+ * For each item, resolves the selected `operation` and dispatches
171
+ * the corresponding Caedral API call. On success, the response is
172
+ * normalized into a flat JSON object and pushed to the output.
173
+ *
174
+ * When "Continue On Fail" is enabled, per-item errors are captured
175
+ * as `{ error: string }` entries in the output; otherwise the
176
+ * error is re-thrown (wrapped in `NodeOperationError` if it is not
177
+ * already a node error).
178
+ *
179
+ * @returns A single-branch array of output items, one per input.
180
+ * @throws {NodeApiError} If the Caedral API returns a non-2xx
181
+ * status and "Continue On Fail" is disabled.
182
+ * @throws {NodeOperationError} For validation or unknown-operation
183
+ * errors, or when wrapping an unexpected runtime error.
184
+ */
185
+ async execute() {
186
+ const items = this.getInputData();
187
+ const returnData = [];
188
+ const credentials = (await this.getCredentials("caedralApi"));
189
+ const baseUrl = (0, helpers_1.normalizeBaseUrl)(credentials.baseUrl);
190
+ const apiKey = credentials.apiKey;
191
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
192
+ try {
193
+ const operation = this.getNodeParameter("operation", itemIndex);
194
+ if (operation === "getUsage") {
195
+ const usage = await caedralRequest(this, baseUrl, apiKey, "GET", "/v1/usage");
196
+ returnData.push({
197
+ json: (0, helpers_1.formatUsageForOutput)(usage),
198
+ pairedItem: { item: itemIndex },
199
+ });
200
+ continue;
201
+ }
202
+ if (operation === "chatCompletion") {
203
+ const model = this.getNodeParameter("model", itemIndex);
204
+ const messageMode = this.getNodeParameter("messageMode", itemIndex);
205
+ const message = this.getNodeParameter("message", itemIndex);
206
+ const messagesJson = this.getNodeParameter("messagesJson", itemIndex);
207
+ const temperature = this.getNodeParameter("temperature", itemIndex);
208
+ const maxTokens = this.getNodeParameter("maxTokens", itemIndex);
209
+ const body = (0, helpers_1.buildChatCompletionBody)({
210
+ model,
211
+ messageMode,
212
+ message,
213
+ messagesJson: messagesJson,
214
+ temperature: temperature === 1 ? undefined : temperature,
215
+ maxTokens: maxTokens > 0 ? maxTokens : undefined,
216
+ });
217
+ const response = await caedralRequest(this, baseUrl, apiKey, "POST", "/v1/chat/completions", body);
218
+ const parsed = (0, helpers_1.parseChatCompletionResponse)(response);
219
+ returnData.push({
220
+ json: {
221
+ content: parsed.content,
222
+ model: parsed.model,
223
+ finishReason: parsed.finishReason,
224
+ usage: parsed.usage,
225
+ raw: parsed.raw,
226
+ },
227
+ pairedItem: { item: itemIndex },
228
+ });
229
+ continue;
230
+ }
231
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, {
232
+ itemIndex,
233
+ });
234
+ }
235
+ catch (error) {
236
+ if (this.continueOnFail()) {
237
+ returnData.push({
238
+ json: {
239
+ error: error instanceof Error ? error.message : String(error),
240
+ },
241
+ pairedItem: { item: itemIndex },
242
+ });
243
+ continue;
244
+ }
245
+ if (error instanceof n8n_workflow_1.NodeApiError || error instanceof n8n_workflow_1.NodeOperationError) {
246
+ throw error;
247
+ }
248
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, {
249
+ itemIndex,
250
+ });
251
+ }
252
+ }
253
+ return [returnData];
254
+ }
255
+ }
256
+ exports.Caedral = Caedral;
257
+ async function caedralRequest(context, baseUrl, apiKey, method, path, body) {
258
+ const url = (0, helpers_1.buildRequestUrl)(baseUrl, path);
259
+ try {
260
+ const requestOptions = {
261
+ method,
262
+ url,
263
+ headers: {
264
+ Authorization: `Bearer ${apiKey}`,
265
+ Accept: "application/json",
266
+ },
267
+ json: true,
268
+ returnFullResponse: true,
269
+ ignoreHttpStatusErrors: true,
270
+ };
271
+ if (body !== undefined) {
272
+ requestOptions.headers["Content-Type"] = "application/json";
273
+ requestOptions.body = body;
274
+ }
275
+ const response = await context.helpers.httpRequest(requestOptions);
276
+ const statusCode = response.statusCode;
277
+ const responseBody = response.body;
278
+ if (statusCode >= 400) {
279
+ throw new n8n_workflow_1.NodeApiError(context.getNode(), {
280
+ message: (0, helpers_1.formatApiErrorMessage)(statusCode, responseBody),
281
+ description: JSON.stringify(responseBody, null, 2),
282
+ httpCode: String(statusCode),
283
+ });
284
+ }
285
+ return responseBody;
286
+ }
287
+ catch (error) {
288
+ if (error instanceof n8n_workflow_1.NodeApiError) {
289
+ throw error;
290
+ }
291
+ throw new n8n_workflow_1.NodeApiError(context.getNode(), {
292
+ message: error instanceof Error
293
+ ? error.message
294
+ : "Unexpected error calling Caedral API",
295
+ });
296
+ }
297
+ }
298
+ //# sourceMappingURL=Caedral.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Caedral.node.js","sourceRoot":"","sources":["../../../nodes/Caedral/Caedral.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ {
2
+ "node": "n8n-nodes-caedral",
3
+ "nodeVersion": "1.0",
4
+ "codexVersion": "1.0",
5
+ "categories": ["Development", "AI"],
6
+ "resources": {
7
+ "primaryDocumentation": [
8
+ {
9
+ "url": "https://caedral.com/docs"
10
+ }
11
+ ],
12
+ "credentialDocumentation": [
13
+ {
14
+ "url": "https://caedral.com/docs/authentication"
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,203 @@
1
+ import { DEFAULT_BASE_URL } from "../../shared/constants";
2
+ export { DEFAULT_BASE_URL };
3
+ export declare const MODEL_OPTIONS: readonly [{
4
+ readonly name: "Base (Free)";
5
+ readonly value: "caedral-base";
6
+ }, {
7
+ readonly name: "Titan";
8
+ readonly value: "caedral-titan";
9
+ }, {
10
+ readonly name: "Olympus";
11
+ readonly value: "caedral-olympus";
12
+ }, {
13
+ readonly name: "Primordial";
14
+ readonly value: "caedral-primordial";
15
+ }];
16
+ export type CaedralModelId = (typeof MODEL_OPTIONS)[number]["value"];
17
+ export type ChatMessage = {
18
+ role: "system" | "user" | "assistant" | "tool";
19
+ content: string;
20
+ name?: string;
21
+ };
22
+ export type ChatCompletionRequestBody = {
23
+ model: string;
24
+ messages: ChatMessage[];
25
+ temperature?: number;
26
+ max_tokens?: number;
27
+ };
28
+ export type ChatCompletionResponse = {
29
+ id?: string;
30
+ model?: string;
31
+ choices?: Array<{
32
+ index?: number;
33
+ message?: {
34
+ role?: string;
35
+ content?: string | null;
36
+ };
37
+ finish_reason?: string | null;
38
+ }>;
39
+ usage?: {
40
+ prompt_tokens?: number;
41
+ completion_tokens?: number;
42
+ total_tokens?: number;
43
+ };
44
+ };
45
+ export type UsageResponse = {
46
+ accountStatus?: string;
47
+ plan?: string;
48
+ planStatus?: string;
49
+ balanceCents?: number;
50
+ weeklyPool?: {
51
+ limit?: number;
52
+ used?: number;
53
+ remaining?: number;
54
+ };
55
+ overage?: {
56
+ enabled?: boolean;
57
+ limitCents?: number | null;
58
+ usedCents?: number;
59
+ remainingCents?: number | null;
60
+ };
61
+ balanceWeightedUnitsAffordable?: number;
62
+ };
63
+ export type CaedralApiErrorBody = {
64
+ error?: {
65
+ type?: string;
66
+ message?: string;
67
+ code?: number;
68
+ };
69
+ };
70
+ /**
71
+ * Normalize a user-supplied base URL for the Caedral API.
72
+ *
73
+ * Trims whitespace, falls back to {@link DEFAULT_BASE_URL} when
74
+ * empty, and strips any trailing slash so that paths can be
75
+ * appended safely.
76
+ *
77
+ * @param baseUrl - Raw base URL from the node credentials.
78
+ * @returns The normalized base URL without a trailing slash.
79
+ */
80
+ export declare function normalizeBaseUrl(baseUrl?: string): string;
81
+ /**
82
+ * Build the JSON body for a `POST /v1/chat/completions` request.
83
+ *
84
+ * Resolves the messages array from either the simple `message`
85
+ * field or the raw `messagesJson`, and includes `temperature` and
86
+ * `max_tokens` only when they are explicitly provided.
87
+ *
88
+ * @param params - Node parameters collected for the chat completion
89
+ * operation.
90
+ * @returns A fully-formed request body ready to send to the API.
91
+ * @throws {Error} If message resolution fails (missing text in
92
+ * Simple mode, or invalid JSON in JSON mode).
93
+ */
94
+ export declare function buildChatCompletionBody(params: {
95
+ model: string;
96
+ messageMode: "simple" | "json";
97
+ message?: string;
98
+ messagesJson?: string | ChatMessage[];
99
+ temperature?: number;
100
+ maxTokens?: number;
101
+ }): ChatCompletionRequestBody;
102
+ /**
103
+ * Resolve the effective chat messages array for a request.
104
+ *
105
+ * In `"simple"` mode a single user message is built from the
106
+ * trimmed `message` string. In `"json"` mode the `messagesJson`
107
+ * input is parsed and validated via {@link parseMessagesJson}.
108
+ *
109
+ * @param messageMode - Which input path to use.
110
+ * @param message - Raw text for Simple mode.
111
+ * @param messagesJson - Raw JSON (string or already-parsed array)
112
+ * for JSON mode.
113
+ * @returns The validated list of chat messages.
114
+ * @throws {Error} If required input is missing or invalid.
115
+ */
116
+ export declare function resolveMessages(messageMode: "simple" | "json", message?: string, messagesJson?: string | ChatMessage[]): ChatMessage[];
117
+ /**
118
+ * Parse and validate the raw `messagesJson` node input.
119
+ *
120
+ * Accepts either a JSON-encoded string or an already-decoded array.
121
+ * Each entry must be an object with a supported `role`
122
+ * (`"system" | "user" | "assistant" | "tool"`) and a string
123
+ * `content`.
124
+ *
125
+ * @param raw - The raw input value provided by the user.
126
+ * @returns The list of validated chat messages.
127
+ * @throws {Error} If the value is missing, is not valid JSON, is
128
+ * not an array, or contains an invalid entry.
129
+ */
130
+ export declare function parseMessagesJson(raw: string | ChatMessage[] | undefined): ChatMessage[];
131
+ /**
132
+ * Flatten a raw chat completion response into the shape emitted by
133
+ * the node.
134
+ *
135
+ * Extracts the first choice's content, the model id, the finish
136
+ * reason, and token usage, while also preserving the full `raw`
137
+ * payload for downstream nodes that need it.
138
+ *
139
+ * @param response - Raw API response body.
140
+ * @returns A flattened, node-friendly representation.
141
+ */
142
+ export declare function parseChatCompletionResponse(response: ChatCompletionResponse): {
143
+ content: string;
144
+ model: string;
145
+ finishReason: string | null;
146
+ usage: ChatCompletionResponse["usage"] | null;
147
+ raw: ChatCompletionResponse;
148
+ };
149
+ /**
150
+ * Normalize a `GET /v1/usage` response for node output.
151
+ *
152
+ * Fills in defaults for any missing fields so downstream nodes can
153
+ * rely on a stable shape (numeric zeros instead of `undefined`,
154
+ * `"unknown"` for missing status strings, etc.).
155
+ *
156
+ * @param usage - Raw usage response from the API.
157
+ * @returns A fully-populated usage object with default values.
158
+ */
159
+ export declare function formatUsageForOutput(usage: UsageResponse): {
160
+ accountStatus: string;
161
+ plan: string;
162
+ planStatus: string;
163
+ balanceCents: number;
164
+ weeklyPool: {
165
+ limit: number;
166
+ used: number;
167
+ remaining: number;
168
+ };
169
+ overage: {
170
+ enabled: boolean;
171
+ limitCents: number | null;
172
+ usedCents: number;
173
+ remainingCents: number | null;
174
+ };
175
+ balanceWeightedUnitsAffordable: number;
176
+ };
177
+ /**
178
+ * Format a human-readable error message from a Caedral API failure.
179
+ *
180
+ * If the body follows the Caedral error envelope
181
+ * (`{ error: { type, message, code } }`), the `type` is prefixed
182
+ * in brackets before the message. Otherwise a generic message
183
+ * including the HTTP status is returned.
184
+ *
185
+ * @param statusCode - HTTP status code of the failing response.
186
+ * @param body - Parsed error body, or the raw text when parsing
187
+ * failed.
188
+ * @returns A human-readable error message suitable for surfacing
189
+ * in n8n.
190
+ */
191
+ export declare function formatApiErrorMessage(statusCode: number, body: CaedralApiErrorBody | string): string;
192
+ /**
193
+ * Compose a full API URL from a base URL and endpoint path.
194
+ *
195
+ * The base URL is passed through {@link normalizeBaseUrl} and the
196
+ * path is prefixed with `/` if it does not already start with one,
197
+ * guaranteeing exactly one slash between the two.
198
+ *
199
+ * @param baseUrl - Base URL for the Caedral API.
200
+ * @param path - Endpoint path (with or without a leading slash).
201
+ * @returns The composed absolute URL.
202
+ */
203
+ export declare function buildRequestUrl(baseUrl: string, path: string): string;
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MODEL_OPTIONS = exports.DEFAULT_BASE_URL = void 0;
4
+ exports.normalizeBaseUrl = normalizeBaseUrl;
5
+ exports.buildChatCompletionBody = buildChatCompletionBody;
6
+ exports.resolveMessages = resolveMessages;
7
+ exports.parseMessagesJson = parseMessagesJson;
8
+ exports.parseChatCompletionResponse = parseChatCompletionResponse;
9
+ exports.formatUsageForOutput = formatUsageForOutput;
10
+ exports.formatApiErrorMessage = formatApiErrorMessage;
11
+ exports.buildRequestUrl = buildRequestUrl;
12
+ const constants_1 = require("../../shared/constants");
13
+ Object.defineProperty(exports, "DEFAULT_BASE_URL", { enumerable: true, get: function () { return constants_1.DEFAULT_BASE_URL; } });
14
+ exports.MODEL_OPTIONS = [
15
+ { name: "Base (Free)", value: "caedral-base" },
16
+ { name: "Titan", value: "caedral-titan" },
17
+ { name: "Olympus", value: "caedral-olympus" },
18
+ { name: "Primordial", value: "caedral-primordial" },
19
+ ];
20
+ /**
21
+ * Normalize a user-supplied base URL for the Caedral API.
22
+ *
23
+ * Trims whitespace, falls back to {@link DEFAULT_BASE_URL} when
24
+ * empty, and strips any trailing slash so that paths can be
25
+ * appended safely.
26
+ *
27
+ * @param baseUrl - Raw base URL from the node credentials.
28
+ * @returns The normalized base URL without a trailing slash.
29
+ */
30
+ function normalizeBaseUrl(baseUrl) {
31
+ const trimmed = ((baseUrl === null || baseUrl === void 0 ? void 0 : baseUrl.trim()) || constants_1.DEFAULT_BASE_URL).replace(/\/$/, "");
32
+ return trimmed;
33
+ }
34
+ /**
35
+ * Build the JSON body for a `POST /v1/chat/completions` request.
36
+ *
37
+ * Resolves the messages array from either the simple `message`
38
+ * field or the raw `messagesJson`, and includes `temperature` and
39
+ * `max_tokens` only when they are explicitly provided.
40
+ *
41
+ * @param params - Node parameters collected for the chat completion
42
+ * operation.
43
+ * @returns A fully-formed request body ready to send to the API.
44
+ * @throws {Error} If message resolution fails (missing text in
45
+ * Simple mode, or invalid JSON in JSON mode).
46
+ */
47
+ function buildChatCompletionBody(params) {
48
+ const messages = resolveMessages(params.messageMode, params.message, params.messagesJson);
49
+ const body = {
50
+ model: params.model,
51
+ messages,
52
+ };
53
+ if (params.temperature !== undefined && params.temperature !== null) {
54
+ body.temperature = params.temperature;
55
+ }
56
+ if (params.maxTokens !== undefined && params.maxTokens !== null) {
57
+ body.max_tokens = params.maxTokens;
58
+ }
59
+ return body;
60
+ }
61
+ /**
62
+ * Resolve the effective chat messages array for a request.
63
+ *
64
+ * In `"simple"` mode a single user message is built from the
65
+ * trimmed `message` string. In `"json"` mode the `messagesJson`
66
+ * input is parsed and validated via {@link parseMessagesJson}.
67
+ *
68
+ * @param messageMode - Which input path to use.
69
+ * @param message - Raw text for Simple mode.
70
+ * @param messagesJson - Raw JSON (string or already-parsed array)
71
+ * for JSON mode.
72
+ * @returns The validated list of chat messages.
73
+ * @throws {Error} If required input is missing or invalid.
74
+ */
75
+ function resolveMessages(messageMode, message, messagesJson) {
76
+ if (messageMode === "simple") {
77
+ const text = message === null || message === void 0 ? void 0 : message.trim();
78
+ if (!text) {
79
+ throw new Error("Message is required in Simple mode.");
80
+ }
81
+ return [{ role: "user", content: text }];
82
+ }
83
+ const parsed = parseMessagesJson(messagesJson);
84
+ if (parsed.length === 0) {
85
+ throw new Error("Messages JSON must contain at least one message.");
86
+ }
87
+ return parsed;
88
+ }
89
+ /**
90
+ * Parse and validate the raw `messagesJson` node input.
91
+ *
92
+ * Accepts either a JSON-encoded string or an already-decoded array.
93
+ * Each entry must be an object with a supported `role`
94
+ * (`"system" | "user" | "assistant" | "tool"`) and a string
95
+ * `content`.
96
+ *
97
+ * @param raw - The raw input value provided by the user.
98
+ * @returns The list of validated chat messages.
99
+ * @throws {Error} If the value is missing, is not valid JSON, is
100
+ * not an array, or contains an invalid entry.
101
+ */
102
+ function parseMessagesJson(raw) {
103
+ if (raw === undefined || raw === null || raw === "") {
104
+ throw new Error("Messages JSON is required in JSON mode.");
105
+ }
106
+ let value = raw;
107
+ if (typeof raw === "string") {
108
+ try {
109
+ value = JSON.parse(raw);
110
+ }
111
+ catch {
112
+ throw new Error("Messages JSON must be valid JSON.");
113
+ }
114
+ }
115
+ if (!Array.isArray(value)) {
116
+ throw new Error("Messages JSON must be an array of message objects.");
117
+ }
118
+ const messages = [];
119
+ for (const [index, item] of value.entries()) {
120
+ if (typeof item !== "object" || item === null) {
121
+ throw new Error(`Message at index ${index} must be an object.`);
122
+ }
123
+ const role = item.role;
124
+ const content = item.content;
125
+ if (typeof role !== "string" || !role.trim()) {
126
+ throw new Error(`Message at index ${index} requires a role.`);
127
+ }
128
+ if (typeof content !== "string") {
129
+ throw new Error(`Message at index ${index} requires string content.`);
130
+ }
131
+ if (!["system", "user", "assistant", "tool"].includes(role)) {
132
+ throw new Error(`Message at index ${index} has invalid role "${role}".`);
133
+ }
134
+ messages.push({ role: role, content });
135
+ }
136
+ return messages;
137
+ }
138
+ /**
139
+ * Flatten a raw chat completion response into the shape emitted by
140
+ * the node.
141
+ *
142
+ * Extracts the first choice's content, the model id, the finish
143
+ * reason, and token usage, while also preserving the full `raw`
144
+ * payload for downstream nodes that need it.
145
+ *
146
+ * @param response - Raw API response body.
147
+ * @returns A flattened, node-friendly representation.
148
+ */
149
+ function parseChatCompletionResponse(response) {
150
+ var _a, _b, _c, _d, _e, _f;
151
+ const choice = (_a = response.choices) === null || _a === void 0 ? void 0 : _a[0];
152
+ return {
153
+ content: (_c = (_b = choice === null || choice === void 0 ? void 0 : choice.message) === null || _b === void 0 ? void 0 : _b.content) !== null && _c !== void 0 ? _c : "",
154
+ model: (_d = response.model) !== null && _d !== void 0 ? _d : "",
155
+ finishReason: (_e = choice === null || choice === void 0 ? void 0 : choice.finish_reason) !== null && _e !== void 0 ? _e : null,
156
+ usage: (_f = response.usage) !== null && _f !== void 0 ? _f : null,
157
+ raw: response,
158
+ };
159
+ }
160
+ /**
161
+ * Normalize a `GET /v1/usage` response for node output.
162
+ *
163
+ * Fills in defaults for any missing fields so downstream nodes can
164
+ * rely on a stable shape (numeric zeros instead of `undefined`,
165
+ * `"unknown"` for missing status strings, etc.).
166
+ *
167
+ * @param usage - Raw usage response from the API.
168
+ * @returns A fully-populated usage object with default values.
169
+ */
170
+ function formatUsageForOutput(usage) {
171
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
172
+ return {
173
+ accountStatus: (_a = usage.accountStatus) !== null && _a !== void 0 ? _a : "unknown",
174
+ plan: (_b = usage.plan) !== null && _b !== void 0 ? _b : "free",
175
+ planStatus: (_c = usage.planStatus) !== null && _c !== void 0 ? _c : "unknown",
176
+ balanceCents: (_d = usage.balanceCents) !== null && _d !== void 0 ? _d : 0,
177
+ weeklyPool: {
178
+ limit: (_f = (_e = usage.weeklyPool) === null || _e === void 0 ? void 0 : _e.limit) !== null && _f !== void 0 ? _f : 0,
179
+ used: (_h = (_g = usage.weeklyPool) === null || _g === void 0 ? void 0 : _g.used) !== null && _h !== void 0 ? _h : 0,
180
+ remaining: (_k = (_j = usage.weeklyPool) === null || _j === void 0 ? void 0 : _j.remaining) !== null && _k !== void 0 ? _k : 0,
181
+ },
182
+ overage: {
183
+ enabled: (_m = (_l = usage.overage) === null || _l === void 0 ? void 0 : _l.enabled) !== null && _m !== void 0 ? _m : false,
184
+ limitCents: (_p = (_o = usage.overage) === null || _o === void 0 ? void 0 : _o.limitCents) !== null && _p !== void 0 ? _p : null,
185
+ usedCents: (_r = (_q = usage.overage) === null || _q === void 0 ? void 0 : _q.usedCents) !== null && _r !== void 0 ? _r : 0,
186
+ remainingCents: (_t = (_s = usage.overage) === null || _s === void 0 ? void 0 : _s.remainingCents) !== null && _t !== void 0 ? _t : null,
187
+ },
188
+ balanceWeightedUnitsAffordable: (_u = usage.balanceWeightedUnitsAffordable) !== null && _u !== void 0 ? _u : 0,
189
+ };
190
+ }
191
+ /**
192
+ * Format a human-readable error message from a Caedral API failure.
193
+ *
194
+ * If the body follows the Caedral error envelope
195
+ * (`{ error: { type, message, code } }`), the `type` is prefixed
196
+ * in brackets before the message. Otherwise a generic message
197
+ * including the HTTP status is returned.
198
+ *
199
+ * @param statusCode - HTTP status code of the failing response.
200
+ * @param body - Parsed error body, or the raw text when parsing
201
+ * failed.
202
+ * @returns A human-readable error message suitable for surfacing
203
+ * in n8n.
204
+ */
205
+ function formatApiErrorMessage(statusCode, body) {
206
+ if (typeof body === "string") {
207
+ return `Caedral API error (${statusCode}): ${body}`;
208
+ }
209
+ const err = body.error;
210
+ if (err === null || err === void 0 ? void 0 : err.message) {
211
+ const type = err.type ? `[${err.type}] ` : "";
212
+ return `${type}${err.message}`;
213
+ }
214
+ return `Caedral API error (${statusCode})`;
215
+ }
216
+ /**
217
+ * Compose a full API URL from a base URL and endpoint path.
218
+ *
219
+ * The base URL is passed through {@link normalizeBaseUrl} and the
220
+ * path is prefixed with `/` if it does not already start with one,
221
+ * guaranteeing exactly one slash between the two.
222
+ *
223
+ * @param baseUrl - Base URL for the Caedral API.
224
+ * @param path - Endpoint path (with or without a leading slash).
225
+ * @returns The composed absolute URL.
226
+ */
227
+ function buildRequestUrl(baseUrl, path) {
228
+ return `${normalizeBaseUrl(baseUrl)}${path.startsWith("/") ? path : `/${path}`}`;
229
+ }
230
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../nodes/Caedral/helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_BASE_URL = "https://api.caedral.com";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_BASE_URL = void 0;
4
+ exports.DEFAULT_BASE_URL = "https://api.caedral.com";
5
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../shared/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,gBAAgB,GAAG,yBAAyB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "n8n-nodes-caedral",
3
+ "version": "0.1.0",
4
+ "description": "n8n community node for Caedral AI — chat completions and usage from your workflows",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "n8n-community-node-package",
8
+ "n8n",
9
+ "caedral",
10
+ "ai",
11
+ "llm",
12
+ "chatgpt",
13
+ "openai"
14
+ ],
15
+ "author": {
16
+ "name": "Caedral",
17
+ "email": "hello@caedral.com"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/caedral/n8n-nodes-caedral"
22
+ },
23
+ "main": "index.js",
24
+ "scripts": {
25
+ "build": "tsc && node scripts/copy-assets.js",
26
+ "dev": "tsc --watch",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "prepublishOnly": "npm run build"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md"
34
+ ],
35
+ "homepage": "https://caedral.com/docs/n8n-overview",
36
+ "bugs": {
37
+ "url": "https://github.com/caedral/n8n-nodes-caedral/issues"
38
+ },
39
+ "n8n": {
40
+ "n8nNodesApiVersion": 1,
41
+ "credentials": [
42
+ "dist/credentials/CaedralApi.credentials.js"
43
+ ],
44
+ "nodes": [
45
+ "dist/nodes/Caedral/Caedral.node.js"
46
+ ]
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20",
50
+ "bcryptjs": "^3.0.3",
51
+ "dotenv": "^16.4.7",
52
+ "n8n-workflow": "^1.82.0",
53
+ "postgres": "^3.4.9",
54
+ "typescript": "^5",
55
+ "vitest": "^3.0.9"
56
+ },
57
+ "peerDependencies": {
58
+ "n8n-workflow": "*"
59
+ }
60
+ }