n8n-nodes-openrouter-custom 1.0.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,140 @@
1
+ # n8n-nodes-openrouter-custom
2
+
3
+ Custom OpenRouter Chat Model node for n8n with built-in usage tracking and extended options.
4
+
5
+ ## Features
6
+
7
+ - **Usage Tracking**: Automatically track token usage and costs via webhook
8
+ - **User ID Support**: Pass custom user identifiers to OpenRouter for tracking
9
+ - **Session Keys**: Group usage by custom session keys (e.g., `article_123`, `batch_xyz`)
10
+ - **Reasoning Models**: Built-in support for reasoning effort settings (o1, etc.)
11
+ - **Custom Model Kwargs**: Pass any additional parameters to the OpenRouter API
12
+
13
+ ## Installation
14
+
15
+ ### Via n8n Community Nodes
16
+
17
+ 1. Go to **Settings** → **Community Nodes**
18
+ 2. Click **Install**
19
+ 3. Enter `n8n-nodes-openrouter-custom`
20
+ 4. Click **Install**
21
+
22
+ ### Manual Installation
23
+
24
+ ```bash
25
+ cd ~/.n8n
26
+ npm install n8n-nodes-openrouter-custom
27
+ ```
28
+
29
+ Then restart n8n.
30
+
31
+ ### From Source
32
+
33
+ ```bash
34
+ git clone https://github.com/yourusername/n8n-nodes-openrouter-custom
35
+ cd n8n-nodes-openrouter-custom
36
+ npm install
37
+ npm run build
38
+ npm link
39
+
40
+ cd ~/.n8n
41
+ npm link n8n-nodes-openrouter-custom
42
+ ```
43
+
44
+ ## Configuration
45
+
46
+ ### Credentials
47
+
48
+ 1. Create new credentials of type **OpenRouter Custom API**
49
+ 2. Enter your OpenRouter API key
50
+ 3. Optionally set a default **Usage Webhook URL**
51
+
52
+ ### Node Options
53
+
54
+ #### Basic Options
55
+ - **Model**: Select from available OpenRouter models
56
+ - **Temperature**: Controls randomness (0-2)
57
+ - **Max Tokens**: Maximum tokens to generate
58
+ - **Top P**: Nucleus sampling parameter
59
+ - **Frequency/Presence Penalty**: Token repetition controls
60
+ - **Response Format**: Text or JSON mode
61
+
62
+ #### Usage Tracking
63
+ - **Enable Usage Tracking**: Toggle tracking on/off
64
+ - **User ID**: Identifier sent to OpenRouter and logged
65
+ - **Session Key**: Custom key for grouping usage data
66
+ - **Webhook URL Override**: Override credentials webhook for this node
67
+ - **Include Cost**: Include cost data in tracking
68
+
69
+ #### Advanced Options
70
+ - **Model Kwargs**: Additional JSON parameters for the API
71
+ - **Reasoning Effort**: For reasoning models (low/medium/high)
72
+
73
+ ## Usage Tracking Webhook
74
+
75
+ When enabled, the node sends a POST request to your webhook URL after each LLM call:
76
+
77
+ ```json
78
+ {
79
+ "timestamp": "2024-01-15T10:30:00.000Z",
80
+ "run_id": "abc123",
81
+ "parent_run_id": "xyz789",
82
+ "model": "anthropic/claude-sonnet-4",
83
+ "user": "user_123",
84
+ "session_key": "article_456",
85
+ "input_tokens": 150,
86
+ "output_tokens": 500,
87
+ "total_tokens": 650,
88
+ "cost": 0.00325,
89
+ "cached_tokens": 50,
90
+ "reasoning_tokens": 0,
91
+ "finish_reason": "stop"
92
+ }
93
+ ```
94
+
95
+ ### Example Webhook Workflow
96
+
97
+ Create an n8n workflow with:
98
+ 1. **Webhook** trigger node (POST)
99
+ 2. **Google Sheets** or **Database** node to store the data
100
+
101
+ ## Example Usage
102
+
103
+ ### Basic Usage
104
+
105
+ 1. Add the **OpenRouter Custom** node to your AI Agent
106
+ 2. Select your model
107
+ 3. Configure options as needed
108
+
109
+ ### With Usage Tracking
110
+
111
+ 1. Set up a webhook workflow to receive usage data
112
+ 2. In credentials, set the **Usage Webhook URL**
113
+ 3. In the node, enable **Usage Tracking**
114
+ 4. Optionally set **User ID** and **Session Key**
115
+
116
+ ### With Reasoning Models
117
+
118
+ 1. Select a reasoning model (e.g., `openai/o1-preview`)
119
+ 2. In **Advanced Options**, set **Reasoning Effort** to `high`
120
+ 3. Set **Temperature** to `1` (required for some reasoning models)
121
+
122
+ ## Development
123
+
124
+ ```bash
125
+ # Install dependencies
126
+ npm install
127
+
128
+ # Build
129
+ npm run build
130
+
131
+ # Watch mode
132
+ npm run dev
133
+
134
+ # Clean build
135
+ npm run clean && npm run build
136
+ ```
137
+
138
+ ## License
139
+
140
+ MIT
@@ -0,0 +1,9 @@
1
+ import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class OpenRouterCustomApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ authenticate: IAuthenticateGeneric;
8
+ test: ICredentialTestRequest;
9
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenRouterCustomApi = void 0;
4
+ class OpenRouterCustomApi {
5
+ constructor() {
6
+ this.name = 'openRouterCustomApi';
7
+ this.displayName = 'OpenRouter Custom API';
8
+ this.documentationUrl = 'https://openrouter.ai/docs';
9
+ this.properties = [
10
+ {
11
+ displayName: 'API Key',
12
+ name: 'apiKey',
13
+ type: 'string',
14
+ typeOptions: {
15
+ password: true,
16
+ },
17
+ required: true,
18
+ default: '',
19
+ description: 'Your OpenRouter API key',
20
+ },
21
+ {
22
+ displayName: 'Base URL',
23
+ name: 'url',
24
+ type: 'string',
25
+ default: 'https://openrouter.ai/api/v1',
26
+ description: 'OpenRouter API base URL',
27
+ },
28
+ {
29
+ displayName: 'Usage Webhook URL',
30
+ name: 'usageWebhookUrl',
31
+ type: 'string',
32
+ default: '',
33
+ description: 'Optional webhook URL to send usage data to (leave empty to disable)',
34
+ },
35
+ ];
36
+ this.authenticate = {
37
+ type: 'generic',
38
+ properties: {
39
+ headers: {
40
+ Authorization: '=Bearer {{$credentials.apiKey}}',
41
+ },
42
+ },
43
+ };
44
+ this.test = {
45
+ request: {
46
+ baseURL: '={{$credentials.url}}',
47
+ url: '/models',
48
+ method: 'GET',
49
+ },
50
+ };
51
+ }
52
+ }
53
+ exports.OpenRouterCustomApi = OpenRouterCustomApi;
54
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3BlblJvdXRlckN1c3RvbUFwaS5jcmVkZW50aWFscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jcmVkZW50aWFscy9PcGVuUm91dGVyQ3VzdG9tQXBpLmNyZWRlbnRpYWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQU9BLE1BQWEsbUJBQW1CO0lBQWhDO1FBQ0MsU0FBSSxHQUFHLHFCQUFxQixDQUFDO1FBQzdCLGdCQUFXLEdBQUcsdUJBQXVCLENBQUM7UUFDdEMscUJBQWdCLEdBQUcsNEJBQTRCLENBQUM7UUFDaEQsZUFBVSxHQUFzQjtZQUMvQjtnQkFDQyxXQUFXLEVBQUUsU0FBUztnQkFDdEIsSUFBSSxFQUFFLFFBQVE7Z0JBQ2QsSUFBSSxFQUFFLFFBQVE7Z0JBQ2QsV0FBVyxFQUFFO29CQUNaLFFBQVEsRUFBRSxJQUFJO2lCQUNkO2dCQUNELFFBQVEsRUFBRSxJQUFJO2dCQUNkLE9BQU8sRUFBRSxFQUFFO2dCQUNYLFdBQVcsRUFBRSx5QkFBeUI7YUFDdEM7WUFDRDtnQkFDQyxXQUFXLEVBQUUsVUFBVTtnQkFDdkIsSUFBSSxFQUFFLEtBQUs7Z0JBQ1gsSUFBSSxFQUFFLFFBQVE7Z0JBQ2QsT0FBTyxFQUFFLDhCQUE4QjtnQkFDdkMsV0FBVyxFQUFFLHlCQUF5QjthQUN0QztZQUNEO2dCQUNDLFdBQVcsRUFBRSxtQkFBbUI7Z0JBQ2hDLElBQUksRUFBRSxpQkFBaUI7Z0JBQ3ZCLElBQUksRUFBRSxRQUFRO2dCQUNkLE9BQU8sRUFBRSxFQUFFO2dCQUNYLFdBQVcsRUFBRSxxRUFBcUU7YUFDbEY7U0FDRCxDQUFDO1FBRUYsaUJBQVksR0FBeUI7WUFDcEMsSUFBSSxFQUFFLFNBQVM7WUFDZixVQUFVLEVBQUU7Z0JBQ1gsT0FBTyxFQUFFO29CQUNSLGFBQWEsRUFBRSxpQ0FBaUM7aUJBQ2hEO2FBQ0Q7U0FDRCxDQUFDO1FBRUYsU0FBSSxHQUEyQjtZQUM5QixPQUFPLEVBQUU7Z0JBQ1IsT0FBTyxFQUFFLHVCQUF1QjtnQkFDaEMsR0FBRyxFQUFFLFNBQVM7Z0JBQ2QsTUFBTSxFQUFFLEtBQUs7YUFDYjtTQUNELENBQUM7SUFDSCxDQUFDO0NBQUE7QUFoREQsa0RBZ0RDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUge1xuXHRJQXV0aGVudGljYXRlR2VuZXJpYyxcblx0SUNyZWRlbnRpYWxUZXN0UmVxdWVzdCxcblx0SUNyZWRlbnRpYWxUeXBlLFxuXHRJTm9kZVByb3BlcnRpZXMsXG59IGZyb20gJ244bi13b3JrZmxvdyc7XG5cbmV4cG9ydCBjbGFzcyBPcGVuUm91dGVyQ3VzdG9tQXBpIGltcGxlbWVudHMgSUNyZWRlbnRpYWxUeXBlIHtcblx0bmFtZSA9ICdvcGVuUm91dGVyQ3VzdG9tQXBpJztcblx0ZGlzcGxheU5hbWUgPSAnT3BlblJvdXRlciBDdXN0b20gQVBJJztcblx0ZG9jdW1lbnRhdGlvblVybCA9ICdodHRwczovL29wZW5yb3V0ZXIuYWkvZG9jcyc7XG5cdHByb3BlcnRpZXM6IElOb2RlUHJvcGVydGllc1tdID0gW1xuXHRcdHtcblx0XHRcdGRpc3BsYXlOYW1lOiAnQVBJIEtleScsXG5cdFx0XHRuYW1lOiAnYXBpS2V5Jyxcblx0XHRcdHR5cGU6ICdzdHJpbmcnLFxuXHRcdFx0dHlwZU9wdGlvbnM6IHtcblx0XHRcdFx0cGFzc3dvcmQ6IHRydWUsXG5cdFx0XHR9LFxuXHRcdFx0cmVxdWlyZWQ6IHRydWUsXG5cdFx0XHRkZWZhdWx0OiAnJyxcblx0XHRcdGRlc2NyaXB0aW9uOiAnWW91ciBPcGVuUm91dGVyIEFQSSBrZXknLFxuXHRcdH0sXG5cdFx0e1xuXHRcdFx0ZGlzcGxheU5hbWU6ICdCYXNlIFVSTCcsXG5cdFx0XHRuYW1lOiAndXJsJyxcblx0XHRcdHR5cGU6ICdzdHJpbmcnLFxuXHRcdFx0ZGVmYXVsdDogJ2h0dHBzOi8vb3BlbnJvdXRlci5haS9hcGkvdjEnLFxuXHRcdFx0ZGVzY3JpcHRpb246ICdPcGVuUm91dGVyIEFQSSBiYXNlIFVSTCcsXG5cdFx0fSxcblx0XHR7XG5cdFx0XHRkaXNwbGF5TmFtZTogJ1VzYWdlIFdlYmhvb2sgVVJMJyxcblx0XHRcdG5hbWU6ICd1c2FnZVdlYmhvb2tVcmwnLFxuXHRcdFx0dHlwZTogJ3N0cmluZycsXG5cdFx0XHRkZWZhdWx0OiAnJyxcblx0XHRcdGRlc2NyaXB0aW9uOiAnT3B0aW9uYWwgd2ViaG9vayBVUkwgdG8gc2VuZCB1c2FnZSBkYXRhIHRvIChsZWF2ZSBlbXB0eSB0byBkaXNhYmxlKScsXG5cdFx0fSxcblx0XTtcblxuXHRhdXRoZW50aWNhdGU6IElBdXRoZW50aWNhdGVHZW5lcmljID0ge1xuXHRcdHR5cGU6ICdnZW5lcmljJyxcblx0XHRwcm9wZXJ0aWVzOiB7XG5cdFx0XHRoZWFkZXJzOiB7XG5cdFx0XHRcdEF1dGhvcml6YXRpb246ICc9QmVhcmVyIHt7JGNyZWRlbnRpYWxzLmFwaUtleX19Jyxcblx0XHRcdH0sXG5cdFx0fSxcblx0fTtcblxuXHR0ZXN0OiBJQ3JlZGVudGlhbFRlc3RSZXF1ZXN0ID0ge1xuXHRcdHJlcXVlc3Q6IHtcblx0XHRcdGJhc2VVUkw6ICc9e3skY3JlZGVudGlhbHMudXJsfX0nLFxuXHRcdFx0dXJsOiAnL21vZGVscycsXG5cdFx0XHRtZXRob2Q6ICdHRVQnLFxuXHRcdH0sXG5cdH07XG59XG4iXX0=
@@ -0,0 +1,2 @@
1
+ export * from './nodes/LmChatOpenRouterCustom/LmChatOpenRouterCustom.node';
2
+ export * from './credentials/OpenRouterCustomApi.credentials';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./nodes/LmChatOpenRouterCustom/LmChatOpenRouterCustom.node"), exports);
18
+ __exportStar(require("./credentials/OpenRouterCustomApi.credentials"), exports);
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDZGQUEyRTtBQUMzRSxnRkFBOEQiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tICcuL25vZGVzL0xtQ2hhdE9wZW5Sb3V0ZXJDdXN0b20vTG1DaGF0T3BlblJvdXRlckN1c3RvbS5ub2RlJztcbmV4cG9ydCAqIGZyb20gJy4vY3JlZGVudGlhbHMvT3BlblJvdXRlckN1c3RvbUFwaS5jcmVkZW50aWFscyc7XG4iXX0=
@@ -0,0 +1,5 @@
1
+ import type { INodeType, INodeTypeDescription, ISupplyDataFunctions, SupplyData } from 'n8n-workflow';
2
+ export declare class LmChatOpenRouterCustom implements INodeType {
3
+ description: INodeTypeDescription;
4
+ supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData>;
5
+ }
@@ -0,0 +1,371 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LmChatOpenRouterCustom = void 0;
4
+ const openai_1 = require("@langchain/openai");
5
+ const n8n_workflow_1 = require("n8n-workflow");
6
+ class LmChatOpenRouterCustom {
7
+ constructor() {
8
+ this.description = {
9
+ displayName: 'OpenRouter Chat Model (Custom)',
10
+ name: 'lmChatOpenRouterCustom',
11
+ icon: 'file:openrouter.svg',
12
+ group: ['transform'],
13
+ version: [1],
14
+ description: 'Custom OpenRouter Chat Model with usage tracking and extended options',
15
+ defaults: {
16
+ name: 'OpenRouter Custom',
17
+ },
18
+ codex: {
19
+ categories: ['AI'],
20
+ subcategories: {
21
+ AI: ['Language Models', 'Root Nodes'],
22
+ 'Language Models': ['Chat Models (Recommended)'],
23
+ },
24
+ resources: {
25
+ primaryDocumentation: [
26
+ {
27
+ url: 'https://openrouter.ai/docs',
28
+ },
29
+ ],
30
+ },
31
+ },
32
+ inputs: [],
33
+ outputs: [n8n_workflow_1.NodeConnectionTypes.AiLanguageModel],
34
+ outputNames: ['Model'],
35
+ credentials: [
36
+ {
37
+ name: 'openRouterCustomApi',
38
+ required: true,
39
+ },
40
+ ],
41
+ requestDefaults: {
42
+ ignoreHttpStatusErrors: true,
43
+ baseURL: '={{ $credentials?.url }}',
44
+ },
45
+ properties: [
46
+ {
47
+ displayName: 'Model',
48
+ name: 'model',
49
+ type: 'options',
50
+ description: 'The model which will generate the completion. <a href="https://openrouter.ai/docs/models">Learn more</a>.',
51
+ typeOptions: {
52
+ loadOptions: {
53
+ routing: {
54
+ request: {
55
+ method: 'GET',
56
+ url: '/models',
57
+ },
58
+ output: {
59
+ postReceive: [
60
+ {
61
+ type: 'rootProperty',
62
+ properties: {
63
+ property: 'data',
64
+ },
65
+ },
66
+ {
67
+ type: 'setKeyValue',
68
+ properties: {
69
+ name: '={{$responseItem.id}}',
70
+ value: '={{$responseItem.id}}',
71
+ },
72
+ },
73
+ {
74
+ type: 'sort',
75
+ properties: {
76
+ key: 'name',
77
+ },
78
+ },
79
+ ],
80
+ },
81
+ },
82
+ },
83
+ },
84
+ routing: {
85
+ send: {
86
+ type: 'body',
87
+ property: 'model',
88
+ },
89
+ },
90
+ default: 'openai/gpt-4.1-mini',
91
+ },
92
+ {
93
+ displayName: 'Options',
94
+ name: 'options',
95
+ placeholder: 'Add Option',
96
+ description: 'Additional options to add',
97
+ type: 'collection',
98
+ default: {},
99
+ options: [
100
+ {
101
+ displayName: 'Frequency Penalty',
102
+ name: 'frequencyPenalty',
103
+ default: 0,
104
+ typeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },
105
+ description: "Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim",
106
+ type: 'number',
107
+ },
108
+ {
109
+ displayName: 'Maximum Number of Tokens',
110
+ name: 'maxTokens',
111
+ default: -1,
112
+ description: 'The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 32,768).',
113
+ type: 'number',
114
+ typeOptions: {
115
+ maxValue: 128000,
116
+ },
117
+ },
118
+ {
119
+ displayName: 'Response Format',
120
+ name: 'responseFormat',
121
+ default: 'text',
122
+ type: 'options',
123
+ options: [
124
+ {
125
+ name: 'Text',
126
+ value: 'text',
127
+ description: 'Regular text response',
128
+ },
129
+ {
130
+ name: 'JSON',
131
+ value: 'json_object',
132
+ description: 'Enables JSON mode, which should guarantee the message the model generates is valid JSON',
133
+ },
134
+ ],
135
+ },
136
+ {
137
+ displayName: 'Presence Penalty',
138
+ name: 'presencePenalty',
139
+ default: 0,
140
+ typeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },
141
+ description: "Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics",
142
+ type: 'number',
143
+ },
144
+ {
145
+ displayName: 'Sampling Temperature',
146
+ name: 'temperature',
147
+ default: 0.7,
148
+ typeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },
149
+ description: 'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',
150
+ type: 'number',
151
+ },
152
+ {
153
+ displayName: 'Timeout',
154
+ name: 'timeout',
155
+ default: 360000,
156
+ description: 'Maximum amount of time a request is allowed to take in milliseconds',
157
+ type: 'number',
158
+ },
159
+ {
160
+ displayName: 'Max Retries',
161
+ name: 'maxRetries',
162
+ default: 2,
163
+ description: 'Maximum number of retries to attempt',
164
+ type: 'number',
165
+ },
166
+ {
167
+ displayName: 'Top P',
168
+ name: 'topP',
169
+ default: 1,
170
+ typeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },
171
+ description: 'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',
172
+ type: 'number',
173
+ },
174
+ ],
175
+ },
176
+ {
177
+ displayName: 'Usage Tracking',
178
+ name: 'usageTracking',
179
+ placeholder: 'Add Usage Tracking Options',
180
+ description: 'Options for tracking token usage and costs',
181
+ type: 'collection',
182
+ default: {},
183
+ options: [
184
+ {
185
+ displayName: 'Enable Usage Tracking',
186
+ name: 'enableTracking',
187
+ type: 'boolean',
188
+ default: true,
189
+ description: 'Whether to track and log usage data',
190
+ },
191
+ {
192
+ displayName: 'User ID',
193
+ name: 'userId',
194
+ type: 'string',
195
+ default: '',
196
+ description: 'User identifier for usage tracking (sent to OpenRouter and logged)',
197
+ },
198
+ {
199
+ displayName: 'Session Key',
200
+ name: 'sessionKey',
201
+ type: 'string',
202
+ default: '',
203
+ description: 'Custom session key for grouping usage (e.g., article_123, batch_xyz)',
204
+ },
205
+ {
206
+ displayName: 'Webhook URL Override',
207
+ name: 'webhookUrlOverride',
208
+ type: 'string',
209
+ default: '',
210
+ description: 'Override the webhook URL from credentials for this specific node',
211
+ },
212
+ {
213
+ displayName: 'Include Cost',
214
+ name: 'includeCost',
215
+ type: 'boolean',
216
+ default: true,
217
+ description: 'Whether to include cost data in usage tracking (requires OpenRouter to return it)',
218
+ },
219
+ ],
220
+ },
221
+ {
222
+ displayName: 'Advanced Options',
223
+ name: 'advancedOptions',
224
+ placeholder: 'Add Advanced Option',
225
+ description: 'Advanced model configuration options',
226
+ type: 'collection',
227
+ default: {},
228
+ options: [
229
+ {
230
+ displayName: 'Model Kwargs (JSON)',
231
+ name: 'modelKwargs',
232
+ type: 'json',
233
+ default: '{}',
234
+ description: 'Additional model kwargs to pass to OpenRouter API as JSON',
235
+ },
236
+ {
237
+ displayName: 'Reasoning Effort',
238
+ name: 'reasoningEffort',
239
+ type: 'options',
240
+ default: '',
241
+ description: 'For reasoning models (o1, etc.), set the reasoning effort level',
242
+ options: [
243
+ {
244
+ name: 'None',
245
+ value: '',
246
+ },
247
+ {
248
+ name: 'Low',
249
+ value: 'low',
250
+ },
251
+ {
252
+ name: 'Medium',
253
+ value: 'medium',
254
+ },
255
+ {
256
+ name: 'High',
257
+ value: 'high',
258
+ },
259
+ ],
260
+ },
261
+ ],
262
+ },
263
+ ],
264
+ };
265
+ }
266
+ async supplyData(itemIndex) {
267
+ const credentials = await this.getCredentials('openRouterCustomApi');
268
+ const modelName = this.getNodeParameter('model', itemIndex);
269
+ const options = this.getNodeParameter('options', itemIndex, {});
270
+ const usageTracking = this.getNodeParameter('usageTracking', itemIndex, {});
271
+ const advancedOptions = this.getNodeParameter('advancedOptions', itemIndex, {});
272
+ // Build configuration
273
+ const configuration = {
274
+ baseURL: credentials.url,
275
+ };
276
+ // Build model kwargs
277
+ let modelKwargs = {};
278
+ // Add response format if specified
279
+ if (options.responseFormat && options.responseFormat !== 'text') {
280
+ modelKwargs.response_format = { type: options.responseFormat };
281
+ }
282
+ // Add user ID if specified
283
+ if (usageTracking.userId) {
284
+ modelKwargs.user = usageTracking.userId;
285
+ }
286
+ // Always request usage data from OpenRouter
287
+ modelKwargs.usage = { include: true };
288
+ // Add reasoning effort if specified
289
+ if (advancedOptions.reasoningEffort) {
290
+ modelKwargs.reasoning_effort = advancedOptions.reasoningEffort;
291
+ }
292
+ // Merge custom model kwargs
293
+ if (advancedOptions.modelKwargs) {
294
+ try {
295
+ const customKwargs = typeof advancedOptions.modelKwargs === 'string'
296
+ ? JSON.parse(advancedOptions.modelKwargs)
297
+ : advancedOptions.modelKwargs;
298
+ modelKwargs = { ...modelKwargs, ...customKwargs };
299
+ }
300
+ catch (error) {
301
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in Model Kwargs', { description: 'Please provide valid JSON for Model Kwargs' });
302
+ }
303
+ }
304
+ // Determine webhook URL
305
+ const webhookUrl = usageTracking.webhookUrlOverride || credentials.usageWebhookUrl || '';
306
+ const enableTracking = usageTracking.enableTracking !== false;
307
+ const sessionKey = usageTracking.sessionKey || '';
308
+ const userId = usageTracking.userId || '';
309
+ // Build callbacks
310
+ const callbacks = [];
311
+ if (enableTracking && webhookUrl) {
312
+ callbacks.push({
313
+ handleLLMEnd: async (output, runId, parentRunId) => {
314
+ try {
315
+ const generation = output.generations?.[0]?.[0];
316
+ if (!generation)
317
+ return;
318
+ const message = generation.message;
319
+ const usageMetadata = message?.usage_metadata;
320
+ const responseMetadata = message?.response_metadata;
321
+ const usageData = {
322
+ timestamp: new Date().toISOString(),
323
+ run_id: runId,
324
+ parent_run_id: parentRunId,
325
+ model: modelName,
326
+ user: userId || undefined,
327
+ session_key: sessionKey || undefined,
328
+ input_tokens: usageMetadata?.input_tokens,
329
+ output_tokens: usageMetadata?.output_tokens,
330
+ total_tokens: usageMetadata?.total_tokens,
331
+ cost: responseMetadata?.usage?.cost,
332
+ cached_tokens: responseMetadata?.usage?.prompt_tokens_details?.cached_tokens,
333
+ reasoning_tokens: responseMetadata?.usage?.completion_tokens_details?.reasoning_tokens,
334
+ finish_reason: responseMetadata?.finish_reason,
335
+ };
336
+ // Send to webhook
337
+ await fetch(webhookUrl, {
338
+ method: 'POST',
339
+ headers: { 'Content-Type': 'application/json' },
340
+ body: JSON.stringify(usageData),
341
+ });
342
+ }
343
+ catch (err) {
344
+ // Log error but don't fail the main request
345
+ console.error('Usage tracking webhook failed:', err);
346
+ }
347
+ },
348
+ });
349
+ }
350
+ // Create the model
351
+ const model = new openai_1.ChatOpenAI({
352
+ apiKey: credentials.apiKey,
353
+ model: modelName,
354
+ configuration,
355
+ timeout: options.timeout ?? 360000,
356
+ maxRetries: options.maxRetries ?? 2,
357
+ temperature: options.temperature,
358
+ maxTokens: options.maxTokens > 0 ? options.maxTokens : undefined,
359
+ topP: options.topP,
360
+ frequencyPenalty: options.frequencyPenalty,
361
+ presencePenalty: options.presencePenalty,
362
+ modelKwargs,
363
+ callbacks,
364
+ });
365
+ return {
366
+ response: model,
367
+ };
368
+ }
369
+ }
370
+ exports.LmChatOpenRouterCustom = LmChatOpenRouterCustom;
371
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTG1DaGF0T3BlblJvdXRlckN1c3RvbS5ub2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL25vZGVzL0xtQ2hhdE9wZW5Sb3V0ZXJDdXN0b20vTG1DaGF0T3BlblJvdXRlckN1c3RvbS5ub2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDhDQUErQztBQVMvQywrQ0FHc0I7QUF3QnRCLE1BQWEsc0JBQXNCO0lBQW5DO1FBQ0MsZ0JBQVcsR0FBeUI7WUFDbkMsV0FBVyxFQUFFLGdDQUFnQztZQUM3QyxJQUFJLEVBQUUsd0JBQXdCO1lBQzlCLElBQUksRUFBRSxxQkFBcUI7WUFDM0IsS0FBSyxFQUFFLENBQUMsV0FBVyxDQUFDO1lBQ3BCLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztZQUNaLFdBQVcsRUFBRSx1RUFBdUU7WUFDcEYsUUFBUSxFQUFFO2dCQUNULElBQUksRUFBRSxtQkFBbUI7YUFDekI7WUFDRCxLQUFLLEVBQUU7Z0JBQ04sVUFBVSxFQUFFLENBQUMsSUFBSSxDQUFDO2dCQUNsQixhQUFhLEVBQUU7b0JBQ2QsRUFBRSxFQUFFLENBQUMsaUJBQWlCLEVBQUUsWUFBWSxDQUFDO29CQUNyQyxpQkFBaUIsRUFBRSxDQUFDLDJCQUEyQixDQUFDO2lCQUNoRDtnQkFDRCxTQUFTLEVBQUU7b0JBQ1Ysb0JBQW9CLEVBQUU7d0JBQ3JCOzRCQUNDLEdBQUcsRUFBRSw0QkFBNEI7eUJBQ2pDO3FCQUNEO2lCQUNEO2FBQ0Q7WUFFRCxNQUFNLEVBQUUsRUFBRTtZQUNWLE9BQU8sRUFBRSxDQUFDLGtDQUFtQixDQUFDLGVBQWUsQ0FBQztZQUM5QyxXQUFXLEVBQUUsQ0FBQyxPQUFPLENBQUM7WUFDdEIsV0FBVyxFQUFFO2dCQUNaO29CQUNDLElBQUksRUFBRSxxQkFBcUI7b0JBQzNCLFFBQVEsRUFBRSxJQUFJO2lCQUNkO2FBQ0Q7WUFDRCxlQUFlLEVBQUU7Z0JBQ2hCLHNCQUFzQixFQUFFLElBQUk7Z0JBQzVC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QUFPO1lBQ04sUUFBUSxFQUFFLEtBQUs7U0FDZixDQUFDO0lBQ0gsQ0FBQztDQUNEO0FBbllELHdEQW1ZQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENoYXRPcGVuQUkgfSBmcm9tICdAbGFuZ2NoYWluL29wZW5haSc7XG5pbXBvcnQgdHlwZSB7IENsaWVudE9wdGlvbnMgfSBmcm9tICdAbGFuZ2NoYWluL29wZW5haSc7XG5pbXBvcnQgdHlwZSB7XG5cdElOb2RlVHlwZSxcblx0SU5vZGVUeXBlRGVzY3JpcHRpb24sXG5cdElTdXBwbHlEYXRhRnVuY3Rpb25zLFxuXHRTdXBwbHlEYXRhLFxuXHRJRGF0YU9iamVjdCxcbn0gZnJvbSAnbjhuLXdvcmtmbG93JztcbmltcG9ydCB7XG5cdE5vZGVDb25uZWN0aW9uVHlwZXMsXG5cdE5vZGVPcGVyYXRpb25FcnJvcixcbn0gZnJvbSAnbjhuLXdvcmtmbG93JztcblxuaW50ZXJmYWNlIE9wZW5Sb3V0ZXJDcmVkZW50aWFsIHtcblx0YXBpS2V5OiBzdHJpbmc7XG5cdHVybDogc3RyaW5nO1xuXHR1c2FnZVdlYmhvb2tVcmw/OiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBVc2FnZURhdGEge1xuXHR0aW1lc3RhbXA6IHN0cmluZztcblx0cnVuX2lkOiBzdHJpbmc7XG5cdHBhcmVudF9ydW5faWQ/OiBzdHJpbmc7XG5cdG1vZGVsOiBzdHJpbmc7XG5cdHVzZXI/OiBzdHJpbmc7XG5cdHNlc3Npb25fa2V5Pzogc3RyaW5nO1xuXHRpbnB1dF90b2tlbnM/OiBudW1iZXI7XG5cdG91dHB1dF90b2tlbnM/OiBudW1iZXI7XG5cdHRvdGFsX3Rva2Vucz86IG51bWJlcjtcblx0Y29zdD86IG51bWJlcjtcblx0Y2FjaGVkX3Rva2Vucz86IG51bWJlcjtcblx0cmVhc29uaW5nX3Rva2Vucz86IG51bWJlcjtcblx0ZmluaXNoX3JlYXNvbj86IHN0cmluZztcbn1cblxuZXhwb3J0IGNsYXNzIExtQ2hhdE9wZW5Sb3V0ZXJDdXN0b20gaW1wbGVtZW50cyBJTm9kZVR5cGUge1xuXHRkZXNjcmlwdGlvbjogSU5vZGVUeXBlRGVzY3JpcHRpb24gPSB7XG5cdFx0ZGlzcGxheU5hbWU6ICdPcGVuUm91dGVyIENoYXQgTW9kZWwgKEN1c3RvbSknLFxuXHRcdG5hbWU6ICdsbUNoYXRPcGVuUm91dGVyQ3VzdG9tJyxcblx0XHRpY29uOiAnZmlsZTpvcGVucm91dGVyLnN2ZycsXG5cdFx0Z3JvdXA6IFsndHJhbnNmb3JtJ10sXG5cdFx0dmVyc2lvbjogWzFdLFxuXHRcdGRlc2NyaXB0aW9uOiAnQ3VzdG9tIE9wZW5Sb3V0ZXIgQ2hhdCBNb2RlbCB3aXRoIHVzYWdlIHRyYWNraW5nIGFuZCBleHRlbmRlZCBvcHRpb25zJyxcblx0XHRkZWZhdWx0czoge1xuXHRcdFx0bmFtZTogJ09wZW5Sb3V0ZXIgQ3VzdG9tJyxcblx0XHR9LFxuXHRcdGNvZGV4OiB7XG5cdFx0XHRjYXRlZ29yaWVzOiBbJ0FJJ10sXG5cdFx0XHRzdWJjYXRlZ29yaWVzOiB7XG5cdFx0XHRcdEFJOiBbJ0xhbmd1YWdlIE1vZGVscycsICdSb290IE5vZGVzJ10sXG5cdFx0XHRcdCdMYW5ndWFnZSBNb2RlbHMnOiBbJ0NoYXQgTW9kZWxzIChSZWNvbW1lbmRlZCknXSxcblx0XHRcdH0sXG5cdFx0XHRyZXNvdXJjZXM6IHtcblx0XHRcdFx0cHJpbWFyeURvY3VtZW50YXRpb246IFtcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHR1cmw6ICdodHRwczovL29wZW5yb3V0ZXIuYWkvZG9jcycsXG5cdFx0XHRcdFx0fSxcblx0XHRcdFx0XSxcblx0XHRcdH0sXG5cdFx0fSxcblxuXHRcdGlucHV0czogW10sXG5cdFx0b3V0cHV0czogW05vZGVDb25uZWN0aW9uVHlwZXMuQWlMYW5ndWFnZU1vZGVsXSxcblx0XHRvdXRwdXROYW1lczogWydNb2RlbCddLFxuXHRcdGNyZWRlbnRpYWxzOiBbXG5cdFx0XHR7XG5cdFx0XHRcdG5hbWU6ICdvcGVuUm91dGVyQ3VzdG9tQXBpJyxcblx0XHRcdFx0cmVxdWlyZWQ6IHRydWUsXG5cdFx0XHR9LFxuXHRcdF0sXG5cdFx0cmVxdWVzdERlZmF1bHRzOiB7XG5cdFx0XHRpZ25vcmVIdHRwU3RhdHVzRXJyb3JzOiB0cnVlLFxuXHRcdFx0YmFzZVVSTDogJz17eyAkY3JlZGVudGlhbHM/LnVybCB9fScsXG5cdFx0fSxcblx0XHRwcm9wZXJ0aWVzOiBbXG5cdFx0XHR7XG5cdFx0XHRcdGRpc3BsYXlOYW1lOiAnTW9kZWwnLFxuXHRcdFx0XHRuYW1lOiAnbW9kZWwnLFxuXHRcdFx0XHR0eXBlOiAnb3B0aW9ucycsXG5cdFx0XHRcdGRlc2NyaXB0aW9uOlxuXHRcdFx0XHRcdCdUaGUgbW9kZWwgd2hpY2ggd2lsbCBnZW5lcmF0ZSB0aGUgY29tcGxldGlvbi4gPGEgaHJlZj1cImh0dHBzOi8vb3BlbnJvdXRlci5haS9kb2NzL21vZGVsc1wiPkxlYXJuIG1vcmU8L2E+LicsXG5cdFx0XHRcdHR5cGVPcHRpb25zOiB7XG5cdFx0XHRcdFx0bG9hZE9wdGlvbnM6IHtcblx0XHRcdFx0XHRcdHJvdXRpbmc6IHtcblx0XHRcdFx0XHRcdFx0cmVxdWVzdDoge1xuXHRcdFx0XHRcdFx0XHRcdG1ldGhvZDogJ0dFVCcsXG5cdFx0XHRcdFx0XHRcdFx0dXJsOiAnL21vZGVscycsXG5cdFx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0XHRcdG91dHB1dDoge1xuXHRcdFx0XHRcdFx0XHRcdHBvc3RSZWNlaXZlOiBbXG5cdFx0XHRcdFx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRcdFx0XHRcdHR5cGU6ICdyb290UHJvcGVydHknLFxuXHRcdFx0XHRcdFx0XHRcdFx0XHRwcm9wZXJ0aWVzOiB7XG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0cHJvcGVydHk6ICdkYXRhJyxcblx0XHRcdFx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRcdFx0XHRcdHR5cGU6ICdzZXRLZXlWYWx1ZScsXG5cdFx0XHRcdFx0XHRcdFx0XHRcdHByb3BlcnRpZXM6IHtcblx0XHRcdFx0XHRcdFx0XHRcdFx0XHRuYW1lOiAnPXt7JHJlc3BvbnNlSXRlbS5pZH19Jyxcblx0XHRcdFx0XHRcdFx0XHRcdFx0XHR2YWx1ZTogJz17eyRyZXNwb25zZUl0ZW0uaWR9fScsXG5cdFx0XHRcdFx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0XHRcdFx0XHR0eXBlOiAnc29ydCcsXG5cdFx0XHRcdFx0XHRcdFx0XHRcdHByb3BlcnRpZXM6IHtcblx0XHRcdFx0XHRcdFx0XHRcdFx0XHRrZXk6ICduYW1lJyxcblx0XHRcdFx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0XHRcdFx0XSxcblx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0fSxcblx0XHRcdFx0fSxcblx0XHRcdFx0cm91dGluZzoge1xuXHRcdFx0XHRcdHNlbmQ6IHtcblx0XHRcdFx0XHRcdHR5cGU6ICdib2R5Jyxcblx0XHRcdFx0XHRcdHByb3BlcnR5OiAnbW9kZWwnLFxuXHRcdFx0XHRcdH0sXG5cdFx0XHRcdH0sXG5cdFx0XHRcdGRlZmF1bHQ6ICdvcGVuYWkvZ3B0LTQuMS1taW5pJyxcblx0XHRcdH0sXG5cdFx0XHR7XG5cdFx0XHRcdGRpc3BsYXlOYW1lOiAnT3B0aW9ucycsXG5cdFx0XHRcdG5hbWU6ICdvcHRpb25zJyxcblx0XHRcdFx0cGxhY2Vob2xkZXI6ICdBZGQgT3B0aW9uJyxcblx0XHRcdFx0ZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIG9wdGlvbnMgdG8gYWRkJyxcblx0XHRcdFx0dHlwZTogJ2NvbGxlY3Rpb24nLFxuXHRcdFx0XHRkZWZhdWx0OiB7fSxcblx0XHRcdFx0b3B0aW9uczogW1xuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGRpc3BsYXlOYW1lOiAnRnJlcXVlbmN5IFBlbmFsdHknLFxuXHRcdFx0XHRcdFx0bmFtZTogJ2ZyZXF1ZW5jeVBlbmFsdHknLFxuXHRcdFx0XHRcdFx0ZGVmYXVsdDogMCxcblx0XHRcdFx0XHRcdHR5cGVPcHRpb25zOiB7IG1heFZhbHVlOiAyLCBtaW5WYWx1ZTogLTIsIG51bWJlclByZWNpc2lvbjogMSB9LFxuXHRcdFx0XHRcdFx0ZGVzY3JpcHRpb246XG5cdFx0XHRcdFx0XHRcdFwiUG9zaXRpdmUgdmFsdWVzIHBlbmFsaXplIG5ldyB0b2tlbnMgYmFzZWQgb24gdGhlaXIgZXhpc3RpbmcgZnJlcXVlbmN5IGluIHRoZSB0ZXh0IHNvIGZhciwgZGVjcmVhc2luZyB0aGUgbW9kZWwncyBsaWtlbGlob29kIHRvIHJlcGVhdCB0aGUgc2FtZSBsaW5lIHZlcmJhdGltXCIsXG5cdFx0XHRcdFx0XHR0eXBlOiAnbnVtYmVyJyxcblx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGRpc3BsYXlOYW1lOiAnTWF4aW11bSBOdW1iZXIgb2YgVG9rZW5zJyxcblx0XHRcdFx0XHRcdG5hbWU6ICdtYXhUb2tlbnMnLFxuXHRcdFx0XHRcdFx0ZGVmYXVsdDogLTEsXG5cdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjpcblx0XHRcdFx0XHRcdFx0J1RoZSBtYXhpbXVtIG51bWJlciBvZiB0b2tlbnMgdG8gZ2VuZXJhdGUgaW4gdGhlIGNvbXBsZXRpb24uIE1vc3QgbW9kZWxzIGhhdmUgYSBjb250ZXh0IGxlbmd0aCBvZiAyMDQ4IHRva2VucyAoZXhjZXB0IGZvciB0aGUgbmV3ZXN0IG1vZGVscywgd2hpY2ggc3VwcG9ydCAzMiw3NjgpLicsXG5cdFx0XHRcdFx0XHR0eXBlOiAnbnVtYmVyJyxcblx0XHRcdFx0XHRcdHR5cGVPcHRpb25zOiB7XG5cdFx0XHRcdFx0XHRcdG1heFZhbHVlOiAxMjgwMDAsXG5cdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0ZGlzcGxheU5hbWU6ICdSZXNwb25zZSBGb3JtYXQnLFxuXHRcdFx0XHRcdFx0bmFtZTogJ3Jlc3BvbnNlRm9ybWF0Jyxcblx0XHRcdFx0XHRcdGRlZmF1bHQ6ICd0ZXh0Jyxcblx0XHRcdFx0XHRcdHR5cGU6ICdvcHRpb25zJyxcblx0XHRcdFx0XHRcdG9wdGlvbnM6IFtcblx0XHRcdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0XHRcdG5hbWU6ICdUZXh0Jyxcblx0XHRcdFx0XHRcdFx0XHR2YWx1ZTogJ3RleHQnLFxuXHRcdFx0XHRcdFx0XHRcdGRlc2NyaXB0aW9uOiAnUmVndWxhciB0ZXh0IHJlc3BvbnNlJyxcblx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0XHRcdG5hbWU6ICdKU09OJyxcblx0XHRcdFx0XHRcdFx0XHR2YWx1ZTogJ2pzb25fb2JqZWN0Jyxcblx0XHRcdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjpcblx0XHRcdFx0XHRcdFx0XHRcdCdFbmFibGVzIEpTT04gbW9kZSwgd2hpY2ggc2hvdWxkIGd1YXJhbnRlZSB0aGUgbWVzc2FnZSB0aGUgbW9kZWwgZ2VuZXJhdGVzIGlzIHZhbGlkIEpTT04nLFxuXHRcdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdFx0XSxcblx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGRpc3BsYXlOYW1lOiAnUHJlc2VuY2UgUGVuYWx0eScsXG5cdFx0XHRcdFx0XHRuYW1lOiAncHJlc2VuY2VQZW5hbHR5Jyxcblx0XHRcdFx0XHRcdGRlZmF1bHQ6IDAsXG5cdFx0XHRcdFx0XHR0eXBlT3B0aW9uczogeyBtYXhWYWx1ZTogMiwgbWluVmFsdWU6IC0yLCBudW1iZXJQcmVjaXNpb246IDEgfSxcblx0XHRcdFx0XHRcdGRlc2NyaXB0aW9uOlxuXHRcdFx0XHRcdFx0XHRcIlBvc2l0aXZlIHZhbHVlcyBwZW5hbGl6ZSBuZXcgdG9rZW5zIGJhc2VkIG9uIHdoZXRoZXIgdGhleSBhcHBlYXIgaW4gdGhlIHRleHQgc28gZmFyLCBpbmNyZWFzaW5nIHRoZSBtb2RlbCdzIGxpa2VsaWhvb2QgdG8gdGFsayBhYm91dCBuZXcgdG9waWNzXCIsXG5cdFx0XHRcdFx0XHR0eXBlOiAnbnVtYmVyJyxcblx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGRpc3BsYXlOYW1lOiAnU2FtcGxpbmcgVGVtcGVyYXR1cmUnLFxuXHRcdFx0XHRcdFx0bmFtZTogJ3RlbXBlcmF0dXJlJyxcblx0XHRcdFx0XHRcdGRlZmF1bHQ6IDAuNyxcblx0XHRcdFx0XHRcdHR5cGVPcHRpb25zOiB7IG1heFZhbHVlOiAyLCBtaW5WYWx1ZTogMCwgbnVtYmVyUHJlY2lzaW9uOiAxIH0sXG5cdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjpcblx0XHRcdFx0XHRcdFx0J0NvbnRyb2xzIHJhbmRvbW5lc3M6IExvd2VyaW5nIHJlc3VsdHMgaW4gbGVzcyByYW5kb20gY29tcGxldGlvbnMuIEFzIHRoZSB0ZW1wZXJhdHVyZSBhcHByb2FjaGVzIHplcm8sIHRoZSBtb2RlbCB3aWxsIGJlY29tZSBkZXRlcm1pbmlzdGljIGFuZCByZXBldGl0aXZlLicsXG5cdFx0XHRcdFx0XHR0eXBlOiAnbnVtYmVyJyxcblx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGRpc3BsYXlOYW1lOiAnVGltZW91dCcsXG5cdFx0XHRcdFx0XHRuYW1lOiAndGltZW91dCcsXG5cdFx0XHRcdFx0XHRkZWZhdWx0OiAzNjAwMDAsXG5cdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjogJ01heGltdW0gYW1vdW50IG9mIHRpbWUgYSByZXF1ZXN0IGlzIGFsbG93ZWQgdG8gdGFrZSBpbiBtaWxsaXNlY29uZHMnLFxuXHRcdFx0XHRcdFx0dHlwZTogJ251bWJlcicsXG5cdFx0XHRcdFx0fSxcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRkaXNwbGF5TmFtZTogJ01heCBSZXRyaWVzJyxcblx0XHRcdFx0XHRcdG5hbWU6ICdtYXhSZXRyaWVzJyxcblx0XHRcdFx0XHRcdGRlZmF1bHQ6IDIsXG5cdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjogJ01heGltdW0gbnVtYmVyIG9mIHJldHJpZXMgdG8gYXR0ZW1wdCcsXG5cdFx0XHRcdFx0XHR0eXBlOiAnbnVtYmVyJyxcblx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGRpc3BsYXlOYW1lOiAnVG9wIFAnLFxuXHRcdFx0XHRcdFx0bmFtZTogJ3RvcFAnLFxuXHRcdFx0XHRcdFx0ZGVmYXVsdDogMSxcblx0XHRcdFx0XHRcdHR5cGVPcHRpb25zOiB7IG1heFZhbHVlOiAxLCBtaW5WYWx1ZTogMCwgbnVtYmVyUHJlY2lzaW9uOiAxIH0sXG5cdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjpcblx0XHRcdFx0XHRcdFx0J0NvbnRyb2xzIGRpdmVyc2l0eSB2aWEgbnVjbGV1cyBzYW1wbGluZzogMC41IG1lYW5zIGhhbGYgb2YgYWxsIGxpa2VsaWhvb2Qtd2VpZ2h0ZWQgb3B0aW9ucyBhcmUgY29uc2lkZXJlZC4gV2UgZ2VuZXJhbGx5IHJlY29tbWVuZCBhbHRlcmluZyB0aGlzIG9yIHRlbXBlcmF0dXJlIGJ1dCBub3QgYm90aC4nLFxuXHRcdFx0XHRcdFx0dHlwZTogJ251bWJlcicsXG5cdFx0XHRcdFx0fSxcblx0XHRcdFx0XSxcblx0XHRcdH0sXG5cdFx0XHR7XG5cdFx0XHRcdGRpc3BsYXlOYW1lOiAnVXNhZ2UgVHJhY2tpbmcnLFxuXHRcdFx0XHRuYW1lOiAndXNhZ2VUcmFja2luZycsXG5cdFx0XHRcdHBsYWNlaG9sZGVyOiAnQWRkIFVzYWdlIFRyYWNraW5nIE9wdGlvbnMnLFxuXHRcdFx0XHRkZXNjcmlwdGlvbjogJ09wdGlvbnMgZm9yIHRyYWNraW5nIHRva2VuIHVzYWdlIGFuZCBjb3N0cycsXG5cdFx0XHRcdHR5cGU6ICdjb2xsZWN0aW9uJyxcblx0XHRcdFx0ZGVmYXVsdDoge30sXG5cdFx0XHRcdG9wdGlvbnM6IFtcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRkaXNwbGF5TmFtZTogJ0VuYWJsZSBVc2FnZSBUcmFja2luZycsXG5cdFx0XHRcdFx0XHRuYW1lOiAnZW5hYmxlVHJhY2tpbmcnLFxuXHRcdFx0XHRcdFx0dHlwZTogJ2Jvb2xlYW4nLFxuXHRcdFx0XHRcdFx0ZGVmYXVsdDogdHJ1ZSxcblx0XHRcdFx0XHRcdGRlc2NyaXB0aW9uOiAnV2hldGhlciB0byB0cmFjayBhbmQgbG9nIHVzYWdlIGRhdGEnLFxuXHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0ZGlzcGxheU5hbWU6ICdVc2VyIElEJyxcblx0XHRcdFx0XHRcdG5hbWU6ICd1c2VySWQnLFxuXHRcdFx0XHRcdFx0dHlwZTogJ3N0cmluZycsXG5cdFx0XHRcdFx0XHRkZWZhdWx0OiAnJyxcblx0XHRcdFx0XHRcdGRlc2NyaXB0aW9uOiAnVXNlciBpZGVudGlmaWVyIGZvciB1c2FnZSB0cmFja2luZyAoc2VudCB0byBPcGVuUm91dGVyIGFuZCBsb2dnZWQpJyxcblx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdGRpc3BsYXlOYW1lOiAnU2Vzc2lvbiBLZXknLFxuXHRcdFx0XHRcdFx0bmFtZTogJ3Nlc3Npb25LZXknLFxuXHRcdFx0XHRcdFx0dHlwZTogJ3N0cmluZycsXG5cdFx0XHRcdFx0XHRkZWZhdWx0OiAnJyxcblx0XHRcdFx0XHRcdGRlc2NyaXB0aW9uOiAnQ3VzdG9tIHNlc3Npb24ga2V5IGZvciBncm91cGluZyB1c2FnZSAoZS5nLiwgYXJ0aWNsZV8xMjMsIGJhdGNoX3h5eiknLFxuXHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0ZGlzcGxheU5hbWU6ICdXZWJob29rIFVSTCBPdmVycmlkZScsXG5cdFx0XHRcdFx0XHRuYW1lOiAnd2ViaG9va1VybE92ZXJyaWRlJyxcblx0XHRcdFx0XHRcdHR5cGU6ICdzdHJpbmcnLFxuXHRcdFx0XHRcdFx0ZGVmYXVsdDogJycsXG5cdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjogJ092ZXJyaWRlIHRoZSB3ZWJob29rIFVSTCBmcm9tIGNyZWRlbnRpYWxzIGZvciB0aGlzIHNwZWNpZmljIG5vZGUnLFxuXHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0ZGlzcGxheU5hbWU6ICdJbmNsdWRlIENvc3QnLFxuXHRcdFx0XHRcdFx0bmFtZTogJ2luY2x1ZGVDb3N0Jyxcblx0XHRcdFx0XHRcdHR5cGU6ICdib29sZWFuJyxcblx0XHRcdFx0XHRcdGRlZmF1bHQ6IHRydWUsXG5cdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjogJ1doZXRoZXIgdG8gaW5jbHVkZSBjb3N0IGRhdGEgaW4gdXNhZ2UgdHJhY2tpbmcgKHJlcXVpcmVzIE9wZW5Sb3V0ZXIgdG8gcmV0dXJuIGl0KScsXG5cdFx0XHRcdFx0fSxcblx0XHRcdFx0XSxcblx0XHRcdH0sXG5cdFx0XHR7XG5cdFx0XHRcdGRpc3BsYXlOYW1lOiAnQWR2YW5jZWQgT3B0aW9ucycsXG5cdFx0XHRcdG5hbWU6ICdhZHZhbmNlZE9wdGlvbnMnLFxuXHRcdFx0XHRwbGFjZWhvbGRlcjogJ0FkZCBBZHZhbmNlZCBPcHRpb24nLFxuXHRcdFx0XHRkZXNjcmlwdGlvbjogJ0FkdmFuY2VkIG1vZGVsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucycsXG5cdFx0XHRcdHR5cGU6ICdjb2xsZWN0aW9uJyxcblx0XHRcdFx0ZGVmYXVsdDoge30sXG5cdFx0XHRcdG9wdGlvbnM6IFtcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRkaXNwbGF5TmFtZTogJ01vZGVsIEt3YXJncyAoSlNPTiknLFxuXHRcdFx0XHRcdFx0bmFtZTogJ21vZGVsS3dhcmdzJyxcblx0XHRcdFx0XHRcdHR5cGU6ICdqc29uJyxcblx0XHRcdFx0XHRcdGRlZmF1bHQ6ICd7fScsXG5cdFx0XHRcdFx0XHRkZXNjcmlwdGlvbjogJ0FkZGl0aW9uYWwgbW9kZWwga3dhcmdzIHRvIHBhc3MgdG8gT3BlblJvdXRlciBBUEkgYXMgSlNPTicsXG5cdFx0XHRcdFx0fSxcblx0XHRcdFx0XHR7XG5cdFx0XHRcdFx0XHRkaXNwbGF5TmFtZTogJ1JlYXNvbmluZyBFZmZvcnQnLFxuXHRcdFx0XHRcdFx0bmFtZTogJ3JlYXNvbmluZ0VmZm9ydCcsXG5cdFx0XHRcdFx0XHR0eXBlOiAnb3B0aW9ucycsXG5cdFx0XHRcdFx0XHRkZWZhdWx0OiAnJyxcblx0XHRcdFx0XHRcdGRlc2NyaXB0aW9uOiAnRm9yIHJlYXNvbmluZyBtb2RlbHMgKG8xLCBldGMuKSwgc2V0IHRoZSByZWFzb25pbmcgZWZmb3J0IGxldmVsJyxcblx0XHRcdFx0XHRcdG9wdGlvbnM6IFtcblx0XHRcdFx0XHRcdFx0e1xuXHRcdFx0XHRcdFx0XHRcdG5hbWU6ICdOb25lJyxcblx0XHRcdFx0XHRcdFx0XHR2YWx1ZTogJycsXG5cdFx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0XHRuYW1lOiAnTG93Jyxcblx0XHRcdFx0XHRcdFx0XHR2YWx1ZTogJ2xvdycsXG5cdFx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0XHRuYW1lOiAnTWVkaXVtJyxcblx0XHRcdFx0XHRcdFx0XHR2YWx1ZTogJ21lZGl1bScsXG5cdFx0XHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0XHRcdHtcblx0XHRcdFx0XHRcdFx0XHRuYW1lOiAnSGlnaCcsXG5cdFx0XHRcdFx0XHRcdFx0dmFsdWU6ICdoaWdoJyxcblx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdF0sXG5cdFx0XHRcdFx0fSxcblx0XHRcdFx0XSxcblx0XHRcdH0sXG5cdFx0XSxcblx0fTtcblxuXHRhc3luYyBzdXBwbHlEYXRhKHRoaXM6IElTdXBwbHlEYXRhRnVuY3Rpb25zLCBpdGVtSW5kZXg6IG51bWJlcik6IFByb21pc2U8U3VwcGx5RGF0YT4ge1xuXHRcdGNvbnN0IGNyZWRlbnRpYWxzID0gYXdhaXQgdGhpcy5nZXRDcmVkZW50aWFsczxPcGVuUm91dGVyQ3JlZGVudGlhbD4oJ29wZW5Sb3V0ZXJDdXN0b21BcGknKTtcblxuXHRcdGNvbnN0IG1vZGVsTmFtZSA9IHRoaXMuZ2V0Tm9kZVBhcmFtZXRlcignbW9kZWwnLCBpdGVtSW5kZXgpIGFzIHN0cmluZztcblx0XHRjb25zdCBvcHRpb25zID0gdGhpcy5nZXROb2RlUGFyYW1ldGVyKCdvcHRpb25zJywgaXRlbUluZGV4LCB7fSkgYXMgSURhdGFPYmplY3Q7XG5cdFx0Y29uc3QgdXNhZ2VUcmFja2luZyA9IHRoaXMuZ2V0Tm9kZVBhcmFtZXRlcigndXNhZ2VUcmFja2luZycsIGl0ZW1JbmRleCwge30pIGFzIElEYXRhT2JqZWN0O1xuXHRcdGNvbnN0IGFkdmFuY2VkT3B0aW9ucyA9IHRoaXMuZ2V0Tm9kZVBhcmFtZXRlcignYWR2YW5jZWRPcHRpb25zJywgaXRlbUluZGV4LCB7fSkgYXMgSURhdGFPYmplY3Q7XG5cblx0XHQvLyBCdWlsZCBjb25maWd1cmF0aW9uXG5cdFx0Y29uc3QgY29uZmlndXJhdGlvbjogQ2xpZW50T3B0aW9ucyA9IHtcblx0XHRcdGJhc2VVUkw6IGNyZWRlbnRpYWxzLnVybCxcblx0XHR9O1xuXG5cdFx0Ly8gQnVpbGQgbW9kZWwga3dhcmdzXG5cdFx0bGV0IG1vZGVsS3dhcmdzOiBJRGF0YU9iamVjdCA9IHt9O1xuXG5cdFx0Ly8gQWRkIHJlc3BvbnNlIGZvcm1hdCBpZiBzcGVjaWZpZWRcblx0XHRpZiAob3B0aW9ucy5yZXNwb25zZUZvcm1hdCAmJiBvcHRpb25zLnJlc3BvbnNlRm9ybWF0ICE9PSAndGV4dCcpIHtcblx0XHRcdG1vZGVsS3dhcmdzLnJlc3BvbnNlX2Zvcm1hdCA9IHsgdHlwZTogb3B0aW9ucy5yZXNwb25zZUZvcm1hdCB9O1xuXHRcdH1cblxuXHRcdC8vIEFkZCB1c2VyIElEIGlmIHNwZWNpZmllZFxuXHRcdGlmICh1c2FnZVRyYWNraW5nLnVzZXJJZCkge1xuXHRcdFx0bW9kZWxLd2FyZ3MudXNlciA9IHVzYWdlVHJhY2tpbmcudXNlcklkO1xuXHRcdH1cblxuXHRcdC8vIEFsd2F5cyByZXF1ZXN0IHVzYWdlIGRhdGEgZnJvbSBPcGVuUm91dGVyXG5cdFx0bW9kZWxLd2FyZ3MudXNhZ2UgPSB7IGluY2x1ZGU6IHRydWUgfTtcblxuXHRcdC8vIEFkZCByZWFzb25pbmcgZWZmb3J0IGlmIHNwZWNpZmllZFxuXHRcdGlmIChhZHZhbmNlZE9wdGlvbnMucmVhc29uaW5nRWZmb3J0KSB7XG5cdFx0XHRtb2RlbEt3YXJncy5yZWFzb25pbmdfZWZmb3J0ID0gYWR2YW5jZWRPcHRpb25zLnJlYXNvbmluZ0VmZm9ydDtcblx0XHR9XG5cblx0XHQvLyBNZXJnZSBjdXN0b20gbW9kZWwga3dhcmdzXG5cdFx0aWYgKGFkdmFuY2VkT3B0aW9ucy5tb2RlbEt3YXJncykge1xuXHRcdFx0dHJ5IHtcblx0XHRcdFx0Y29uc3QgY3VzdG9tS3dhcmdzID0gdHlwZW9mIGFkdmFuY2VkT3B0aW9ucy5tb2RlbEt3YXJncyA9PT0gJ3N0cmluZydcblx0XHRcdFx0XHQ/IEpTT04ucGFyc2UoYWR2YW5jZWRPcHRpb25zLm1vZGVsS3dhcmdzKVxuXHRcdFx0XHRcdDogYWR2YW5jZWRPcHRpb25zLm1vZGVsS3dhcmdzO1xuXHRcdFx0XHRtb2RlbEt3YXJncyA9IHsgLi4ubW9kZWxLd2FyZ3MsIC4uLmN1c3RvbUt3YXJncyB9O1xuXHRcdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdFx0dGhyb3cgbmV3IE5vZGVPcGVyYXRpb25FcnJvcihcblx0XHRcdFx0XHR0aGlzLmdldE5vZGUoKSxcblx0XHRcdFx0XHQnSW52YWxpZCBKU09OIGluIE1vZGVsIEt3YXJncycsXG5cdFx0XHRcdFx0eyBkZXNjcmlwdGlvbjogJ1BsZWFzZSBwcm92aWRlIHZhbGlkIEpTT04gZm9yIE1vZGVsIEt3YXJncycgfVxuXHRcdFx0XHQpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIERldGVybWluZSB3ZWJob29rIFVSTFxuXHRcdGNvbnN0IHdlYmhvb2tVcmwgPSAodXNhZ2VUcmFja2luZy53ZWJob29rVXJsT3ZlcnJpZGUgYXMgc3RyaW5nKSB8fCBjcmVkZW50aWFscy51c2FnZVdlYmhvb2tVcmwgfHwgJyc7XG5cdFx0Y29uc3QgZW5hYmxlVHJhY2tpbmcgPSB1c2FnZVRyYWNraW5nLmVuYWJsZVRyYWNraW5nICE9PSBmYWxzZTtcblx0XHRjb25zdCBzZXNzaW9uS2V5ID0gdXNhZ2VUcmFja2luZy5zZXNzaW9uS2V5IGFzIHN0cmluZyB8fCAnJztcblx0XHRjb25zdCB1c2VySWQgPSB1c2FnZVRyYWNraW5nLnVzZXJJZCBhcyBzdHJpbmcgfHwgJyc7XG5cblx0XHQvLyBCdWlsZCBjYWxsYmFja3Ncblx0XHRjb25zdCBjYWxsYmFja3M6IGFueVtdID0gW107XG5cblx0XHRpZiAoZW5hYmxlVHJhY2tpbmcgJiYgd2ViaG9va1VybCkge1xuXHRcdFx0Y2FsbGJhY2tzLnB1c2goe1xuXHRcdFx0XHRoYW5kbGVMTE1FbmQ6IGFzeW5jIChvdXRwdXQ6IGFueSwgcnVuSWQ6IHN0cmluZywgcGFyZW50UnVuSWQ/OiBzdHJpbmcpID0+IHtcblx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0Y29uc3QgZ2VuZXJhdGlvbiA9IG91dHB1dC5nZW5lcmF0aW9ucz8uWzBdPy5bMF07XG5cdFx0XHRcdFx0XHRpZiAoIWdlbmVyYXRpb24pIHJldHVybjtcblxuXHRcdFx0XHRcdFx0Y29uc3QgbWVzc2FnZSA9IGdlbmVyYXRpb24ubWVzc2FnZTtcblx0XHRcdFx0XHRcdGNvbnN0IHVzYWdlTWV0YWRhdGEgPSBtZXNzYWdlPy51c2FnZV9tZXRhZGF0YTtcblx0XHRcdFx0XHRcdGNvbnN0IHJlc3BvbnNlTWV0YWRhdGEgPSBtZXNzYWdlPy5yZXNwb25zZV9tZXRhZGF0YTtcblxuXHRcdFx0XHRcdFx0Y29uc3QgdXNhZ2VEYXRhOiBVc2FnZURhdGEgPSB7XG5cdFx0XHRcdFx0XHRcdHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuXHRcdFx0XHRcdFx0XHRydW5faWQ6IHJ1bklkLFxuXHRcdFx0XHRcdFx0XHRwYXJlbnRfcnVuX2lkOiBwYXJlbnRSdW5JZCxcblx0XHRcdFx0XHRcdFx0bW9kZWw6IG1vZGVsTmFtZSxcblx0XHRcdFx0XHRcdFx0dXNlcjogdXNlcklkIHx8IHVuZGVmaW5lZCxcblx0XHRcdFx0XHRcdFx0c2Vzc2lvbl9rZXk6IHNlc3Npb25LZXkgfHwgdW5kZWZpbmVkLFxuXHRcdFx0XHRcdFx0XHRpbnB1dF90b2tlbnM6IHVzYWdlTWV0YWRhdGE/LmlucHV0X3Rva2Vucyxcblx0XHRcdFx0XHRcdFx0b3V0cHV0X3Rva2VuczogdXNhZ2VNZXRhZGF0YT8ub3V0cHV0X3Rva2Vucyxcblx0XHRcdFx0XHRcdFx0dG90YWxfdG9rZW5zOiB1c2FnZU1ldGFkYXRhPy50b3RhbF90b2tlbnMsXG5cdFx0XHRcdFx0XHRcdGNvc3Q6IHJlc3BvbnNlTWV0YWRhdGE/LnVzYWdlPy5jb3N0LFxuXHRcdFx0XHRcdFx0XHRjYWNoZWRfdG9rZW5zOiByZXNwb25zZU1ldGFkYXRhPy51c2FnZT8ucHJvbXB0X3Rva2Vuc19kZXRhaWxzPy5jYWNoZWRfdG9rZW5zLFxuXHRcdFx0XHRcdFx0XHRyZWFzb25pbmdfdG9rZW5zOiByZXNwb25zZU1ldGFkYXRhPy51c2FnZT8uY29tcGxldGlvbl90b2tlbnNfZGV0YWlscz8ucmVhc29uaW5nX3Rva2Vucyxcblx0XHRcdFx0XHRcdFx0ZmluaXNoX3JlYXNvbjogcmVzcG9uc2VNZXRhZGF0YT8uZmluaXNoX3JlYXNvbixcblx0XHRcdFx0XHRcdH07XG5cblx0XHRcdFx0XHRcdC8vIFNlbmQgdG8gd2ViaG9va1xuXHRcdFx0XHRcdFx0YXdhaXQgZmV0Y2god2ViaG9va1VybCwge1xuXHRcdFx0XHRcdFx0XHRtZXRob2Q6ICdQT1NUJyxcblx0XHRcdFx0XHRcdFx0aGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sXG5cdFx0XHRcdFx0XHRcdGJvZHk6IEpTT04uc3RyaW5naWZ5KHVzYWdlRGF0YSksXG5cdFx0XHRcdFx0XHR9KTtcblx0XHRcdFx0XHR9IGNhdGNoIChlcnIpIHtcblx0XHRcdFx0XHRcdC8vIExvZyBlcnJvciBidXQgZG9uJ3QgZmFpbCB0aGUgbWFpbiByZXF1ZXN0XG5cdFx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCdVc2FnZSB0cmFja2luZyB3ZWJob29rIGZhaWxlZDonLCBlcnIpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSxcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdC8vIENyZWF0ZSB0aGUgbW9kZWxcblx0XHRjb25zdCBtb2RlbCA9IG5ldyBDaGF0T3BlbkFJKHtcblx0XHRcdGFwaUtleTogY3JlZGVudGlhbHMuYXBpS2V5LFxuXHRcdFx0bW9kZWw6IG1vZGVsTmFtZSxcblx0XHRcdGNvbmZpZ3VyYXRpb24sXG5cdFx0XHR0aW1lb3V0OiAob3B0aW9ucy50aW1lb3V0IGFzIG51bWJlcikgPz8gMzYwMDAwLFxuXHRcdFx0bWF4UmV0cmllczogKG9wdGlvbnMubWF4UmV0cmllcyBhcyBudW1iZXIpID8/IDIsXG5cdFx0XHR0ZW1wZXJhdHVyZTogb3B0aW9ucy50ZW1wZXJhdHVyZSBhcyBudW1iZXIsXG5cdFx0XHRtYXhUb2tlbnM6IChvcHRpb25zLm1heFRva2VucyBhcyBudW1iZXIpID4gMCA/IChvcHRpb25zLm1heFRva2VucyBhcyBudW1iZXIpIDogdW5kZWZpbmVkLFxuXHRcdFx0dG9wUDogb3B0aW9ucy50b3BQIGFzIG51bWJlcixcblx0XHRcdGZyZXF1ZW5jeVBlbmFsdHk6IG9wdGlvbnMuZnJlcXVlbmN5UGVuYWx0eSBhcyBudW1iZXIsXG5cdFx0XHRwcmVzZW5jZVBlbmFsdHk6IG9wdGlvbnMucHJlc2VuY2VQZW5hbHR5IGFzIG51bWJlcixcblx0XHRcdG1vZGVsS3dhcmdzLFxuXHRcdFx0Y2FsbGJhY2tzLFxuXHRcdH0pO1xuXG5cdFx0cmV0dXJuIHtcblx0XHRcdHJlc3BvbnNlOiBtb2RlbCxcblx0XHR9O1xuXHR9XG59XG4iXX0=
@@ -0,0 +1 @@
1
+ <svg fill="white" fill-rule="evenodd" width="40" height="40" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>OpenRouter</title><path d="M16.804 1.957l7.22 4.105v.087L16.73 10.21l.017-2.117-.821-.03c-1.059-.028-1.611.002-2.268.11-1.064.175-2.038.577-3.147 1.352L8.345 11.03c-.284.195-.495.336-.68.455l-.515.322-.397.234.385.23.53.338c.476.314 1.17.796 2.701 1.866 1.11.775 2.083 1.177 3.147 1.352l.3.045c.694.091 1.375.094 2.825.033l.022-2.159 7.22 4.105v.087L16.589 22l.014-1.862-.635.022c-1.386.042-2.137.002-3.138-.162-1.694-.28-3.26-.926-4.881-2.059l-2.158-1.5a21.997 21.997 0 00-.755-.498l-.467-.28a55.927 55.927 0 00-.76-.43C2.908 14.73.563 14.116 0 14.116V9.888l.14.004c.564-.007 2.91-.622 3.809-1.124l1.016-.58.438-.274c.428-.28 1.072-.726 2.686-1.853 1.621-1.133 3.186-1.78 4.881-2.059 1.152-.19 1.974-.213 3.814-.138l.02-1.907z"></path></svg>
@@ -0,0 +1 @@
1
+ <svg fill="#94A3B8" fill-rule="evenodd" width="40" height="40" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>OpenRouter</title><path d="M16.804 1.957l7.22 4.105v.087L16.73 10.21l.017-2.117-.821-.03c-1.059-.028-1.611.002-2.268.11-1.064.175-2.038.577-3.147 1.352L8.345 11.03c-.284.195-.495.336-.68.455l-.515.322-.397.234.385.23.53.338c.476.314 1.17.796 2.701 1.866 1.11.775 2.083 1.177 3.147 1.352l.3.045c.694.091 1.375.094 2.825.033l.022-2.159 7.22 4.105v.087L16.589 22l.014-1.862-.635.022c-1.386.042-2.137.002-3.138-.162-1.694-.28-3.26-.926-4.881-2.059l-2.158-1.5a21.997 21.997 0 00-.755-.498l-.467-.28a55.927 55.927 0 00-.76-.43C2.908 14.73.563 14.116 0 14.116V9.888l.14.004c.564-.007 2.91-.622 3.809-1.124l1.016-.58.438-.274c.428-.28 1.072-.726 2.686-1.853 1.621-1.133 3.186-1.78 4.881-2.059 1.152-.19 1.974-.213 3.814-.138l.02-1.907z"></path></svg>
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "n8n-nodes-openrouter-custom",
3
+ "author": "My Author",
4
+ "version": "1.0.0",
5
+ "description": "Custom OpenRouter Chat Model node with usage tracking and extended options",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "n8n": {
10
+ "n8nNodesApiVersion": 1,
11
+ "nodes": [
12
+ "dist/nodes/LmChatOpenRouterCustom/LmChatOpenRouterCustom.node.js"
13
+ ],
14
+ "credentials": [
15
+ "dist/credentials/OpenRouterCustomApi.credentials.js"
16
+ ]
17
+ },
18
+ "scripts": {
19
+ "build": "tsc && npm run copy-icons",
20
+ "copy-icons": "cp src/nodes/LmChatOpenRouterCustom/*.svg dist/nodes/LmChatOpenRouterCustom/ 2>/dev/null || true",
21
+ "dev": "tsc --watch",
22
+ "prepublishOnly": "npm run build",
23
+ "lint": "eslint . --ext .ts",
24
+ "clean": "rm -rf dist"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "dependencies": {
30
+ "@langchain/openai": "^0.3.0"
31
+ },
32
+ "peerDependencies": {
33
+ "n8n-workflow": "^1.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "*",
37
+ "typescript": "*",
38
+ "n8n-workflow": "^1.0.0"
39
+ },
40
+ "keywords": [
41
+ "n8n",
42
+ "n8n-community-node-package",
43
+ "openrouter",
44
+ "langchain",
45
+ "llm",
46
+ "ai"
47
+ ],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/yourusername/n8n-nodes-openrouter-custom"
51
+ }
52
+ }