@heebai/n8n-nodes-heeb 26.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/credentials/HeebApi.credentials.js +37 -0
- package/dist/index.js +7 -0
- package/dist/nodes/heeb/Heeb.node.js +228 -0
- package/dist/nodes/heeb/errors.js +83 -0
- package/dist/nodes/heeb/heeb.svg +20 -0
- package/dist/nodes/heeb/poll.js +34 -0
- package/dist/nodes/heeb/transport.js +18 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Heeb
|
|
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,48 @@
|
|
|
1
|
+
<!-- vale Vale.Spelling = NO -->
|
|
2
|
+
|
|
3
|
+
# @heebai/n8n-nodes-heeb
|
|
4
|
+
|
|
5
|
+
An [n8n](https://n8n.io) community node for the [Heeb](https://heeb.ai) LLM Mentions API.
|
|
6
|
+
|
|
7
|
+
Heeb measures how a brand is referenced inside AI answers, the way classic SEO tools measure Google rankings. This node submits a query to Heeb, optionally waits for the aggregated result, and returns per-model answers, sources, citations, mentions, and the combined visibility and sentiment scores.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Follow the [community nodes installation guide](https://docs.n8n.io/integrations/community-nodes/installation/). In a self-hosted n8n, open **Settings > Community Nodes**, choose **Install**, and enter `@heebai/n8n-nodes-heeb`.
|
|
12
|
+
|
|
13
|
+
## Credential
|
|
14
|
+
|
|
15
|
+
Create a **Heeb API** credential and paste an API key from the API Keys page in the Heeb dashboard. The key travels as the `x-api-key` header on every request. The credential test sends one lightweight call to confirm the key is accepted. Leave **Base URL** at `https://heeb.ai` unless you target a proxied deployment.
|
|
16
|
+
|
|
17
|
+
## Operations
|
|
18
|
+
|
|
19
|
+
### Submit Query
|
|
20
|
+
|
|
21
|
+
Submit a prompt, the entity to track, one or more models, and an optional country code.
|
|
22
|
+
|
|
23
|
+
- With **Wait for Completion** on, the node polls `GET /api/query/{job_id}` until the job settles, then returns the full result. Heeb jobs settle in 30 seconds to 2 minutes; tune **Timeout** and **Poll Interval** to match.
|
|
24
|
+
- With **Wait for Completion** off, the node returns the `job_id` right away. Fetch the result later with **Get Result**.
|
|
25
|
+
|
|
26
|
+
**Models** is a comma-separated list of Heeb model IDs, for example `openai/gpt-5.6-luna, google/ai-mode`. The sellable catalog changes over time, so read the Heeb docs for the current list rather than pinning one in the workflow.
|
|
27
|
+
|
|
28
|
+
### Get Result
|
|
29
|
+
|
|
30
|
+
Fetch a job by its `job_id`. The response is `{ "status": "pending" }` while the job runs, or the aggregated result once it settles.
|
|
31
|
+
|
|
32
|
+
## Errors
|
|
33
|
+
|
|
34
|
+
The node maps Heeb failures onto typed n8n errors:
|
|
35
|
+
|
|
36
|
+
- A rejected API key becomes a node error that points you back to the credential.
|
|
37
|
+
- An out-of-credits response becomes a node error that asks you to top up.
|
|
38
|
+
- A wait that outlives its timeout becomes a node error; the `job_id` stays valid, so fetch it later with **Get Result**.
|
|
39
|
+
|
|
40
|
+
Turn on **Continue On Fail** to collect errors per item instead of stopping the workflow.
|
|
41
|
+
|
|
42
|
+
## Setup help
|
|
43
|
+
|
|
44
|
+
Setup walkthrough and an example workflow you can import: <https://heeb.ai/integrations/n8n>
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HeebApi = void 0;
|
|
4
|
+
class HeebApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = "heebApi";
|
|
7
|
+
this.displayName = "Heeb API";
|
|
8
|
+
this.documentationUrl = "https://heeb.ai/docs";
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: "API Key",
|
|
12
|
+
name: "apiKey",
|
|
13
|
+
type: "string",
|
|
14
|
+
typeOptions: { password: true },
|
|
15
|
+
default: "",
|
|
16
|
+
required: true,
|
|
17
|
+
description: "Heeb API key, created under API Keys in the Heeb dashboard. Sent as the x-api-key header.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: "Base URL",
|
|
21
|
+
name: "baseUrl",
|
|
22
|
+
type: "string",
|
|
23
|
+
default: "https://heeb.ai",
|
|
24
|
+
description: "Base URL of the Heeb API. Change this only for a self-hosted or proxied deployment.",
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
this.authenticate = {
|
|
28
|
+
type: "generic",
|
|
29
|
+
properties: {
|
|
30
|
+
headers: {
|
|
31
|
+
"x-api-key": "={{$credentials.apiKey}}",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.HeebApi = HeebApi;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Heeb = exports.HeebApi = void 0;
|
|
4
|
+
var HeebApi_credentials_1 = require("./credentials/HeebApi.credentials");
|
|
5
|
+
Object.defineProperty(exports, "HeebApi", { enumerable: true, get: function () { return HeebApi_credentials_1.HeebApi; } });
|
|
6
|
+
var Heeb_node_1 = require("./nodes/heeb/Heeb.node");
|
|
7
|
+
Object.defineProperty(exports, "Heeb", { enumerable: true, get: function () { return Heeb_node_1.Heeb; } });
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Heeb = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const poll_1 = require("./poll");
|
|
7
|
+
const transport_1 = require("./transport");
|
|
8
|
+
class Heeb {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.description = {
|
|
11
|
+
displayName: "Heeb",
|
|
12
|
+
name: "heeb",
|
|
13
|
+
icon: "file:heeb.svg",
|
|
14
|
+
group: ["transform"],
|
|
15
|
+
version: 1,
|
|
16
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
17
|
+
description: "Measure how a brand is mentioned across LLM answers with the Heeb LLM Mentions API",
|
|
18
|
+
defaults: {
|
|
19
|
+
name: "Heeb",
|
|
20
|
+
},
|
|
21
|
+
inputs: ["main"],
|
|
22
|
+
outputs: ["main"],
|
|
23
|
+
credentials: [
|
|
24
|
+
{
|
|
25
|
+
name: "heebApi",
|
|
26
|
+
required: true,
|
|
27
|
+
testedBy: "heebApiTest",
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
properties: [
|
|
31
|
+
{
|
|
32
|
+
displayName: "Operation",
|
|
33
|
+
name: "operation",
|
|
34
|
+
type: "options",
|
|
35
|
+
noDataExpression: true,
|
|
36
|
+
options: [
|
|
37
|
+
{
|
|
38
|
+
name: "Get Result",
|
|
39
|
+
value: "get",
|
|
40
|
+
action: "Get a query result by job ID",
|
|
41
|
+
description: "Fetch a job result by job_id, pending or settled",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "Submit Query",
|
|
45
|
+
value: "submit",
|
|
46
|
+
action: "Submit a query",
|
|
47
|
+
description: "Submit a query and optionally wait for the aggregated result",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
default: "submit",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
displayName: "Prompt",
|
|
54
|
+
name: "prompt",
|
|
55
|
+
type: "string",
|
|
56
|
+
typeOptions: { rows: 2 },
|
|
57
|
+
default: "",
|
|
58
|
+
required: true,
|
|
59
|
+
placeholder: "what are the best running shoes in 2026?",
|
|
60
|
+
description: "The buyer question to ask each model",
|
|
61
|
+
displayOptions: { show: { operation: ["submit"] } },
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
displayName: "Entity Name",
|
|
65
|
+
name: "entityName",
|
|
66
|
+
type: "string",
|
|
67
|
+
default: "",
|
|
68
|
+
required: true,
|
|
69
|
+
placeholder: "Nike",
|
|
70
|
+
description: "Brand or entity to track in the answers",
|
|
71
|
+
displayOptions: { show: { operation: ["submit"] } },
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
displayName: "Entity URL",
|
|
75
|
+
name: "entityUrl",
|
|
76
|
+
type: "string",
|
|
77
|
+
default: "",
|
|
78
|
+
required: true,
|
|
79
|
+
placeholder: "https://nike.com",
|
|
80
|
+
description: "Official URL for the entity",
|
|
81
|
+
displayOptions: { show: { operation: ["submit"] } },
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
displayName: "Models",
|
|
85
|
+
name: "models",
|
|
86
|
+
type: "string",
|
|
87
|
+
default: "",
|
|
88
|
+
required: true,
|
|
89
|
+
placeholder: "openai/gpt-5.6-luna, google/ai-mode",
|
|
90
|
+
description: "Comma-separated Heeb model IDs to query. The sellable catalog changes over time; see the Heeb docs for the current list.",
|
|
91
|
+
displayOptions: { show: { operation: ["submit"] } },
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
displayName: "Country Code",
|
|
95
|
+
name: "country",
|
|
96
|
+
type: "string",
|
|
97
|
+
default: "US",
|
|
98
|
+
description: "ISO country code for the geographic context",
|
|
99
|
+
displayOptions: { show: { operation: ["submit"] } },
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
displayName: "Wait for Completion",
|
|
103
|
+
name: "waitForCompletion",
|
|
104
|
+
type: "boolean",
|
|
105
|
+
default: true,
|
|
106
|
+
description: "Whether to poll until the job settles and return the full result. Turn off to return the job_id immediately.",
|
|
107
|
+
displayOptions: { show: { operation: ["submit"] } },
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
displayName: "Timeout",
|
|
111
|
+
name: "timeout",
|
|
112
|
+
type: "number",
|
|
113
|
+
typeOptions: { minValue: 1 },
|
|
114
|
+
default: 120,
|
|
115
|
+
description: "Seconds to keep polling before giving up. Heeb jobs usually settle in 30 to 120 seconds.",
|
|
116
|
+
displayOptions: {
|
|
117
|
+
show: { operation: ["submit"], waitForCompletion: [true] },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
displayName: "Poll Interval",
|
|
122
|
+
name: "pollInterval",
|
|
123
|
+
type: "number",
|
|
124
|
+
typeOptions: { minValue: 1 },
|
|
125
|
+
default: 5,
|
|
126
|
+
description: "Seconds to wait between result checks while polling",
|
|
127
|
+
displayOptions: {
|
|
128
|
+
show: { operation: ["submit"], waitForCompletion: [true] },
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
displayName: "Job ID",
|
|
133
|
+
name: "jobId",
|
|
134
|
+
type: "string",
|
|
135
|
+
default: "",
|
|
136
|
+
required: true,
|
|
137
|
+
description: "The job_id returned by Submit Query",
|
|
138
|
+
displayOptions: { show: { operation: ["get"] } },
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
this.methods = {
|
|
143
|
+
credentialTest: {
|
|
144
|
+
async heebApiTest(credential) {
|
|
145
|
+
const data = credential.data ?? {};
|
|
146
|
+
const baseUrl = String(data.baseUrl || transport_1.DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
147
|
+
try {
|
|
148
|
+
await this.helpers.request({
|
|
149
|
+
method: "GET",
|
|
150
|
+
uri: `${baseUrl}/api/query/00000000-0000-0000-0000-000000000000`,
|
|
151
|
+
headers: { "x-api-key": String(data.apiKey ?? "") },
|
|
152
|
+
json: true,
|
|
153
|
+
});
|
|
154
|
+
return { status: "OK", message: "Heeb API key is valid" };
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const code = Number(error.statusCode ?? error.httpCode ?? error.cause?.statusCode);
|
|
158
|
+
if (code === 401 || code === 403) {
|
|
159
|
+
return { status: "Error", message: "Invalid Heeb API key" };
|
|
160
|
+
}
|
|
161
|
+
return { status: "OK", message: "Heeb API key is valid" };
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async execute() {
|
|
168
|
+
const items = this.getInputData();
|
|
169
|
+
const returnData = [];
|
|
170
|
+
for (let i = 0; i < items.length; i++) {
|
|
171
|
+
try {
|
|
172
|
+
const operation = this.getNodeParameter("operation", i);
|
|
173
|
+
if (operation === "submit") {
|
|
174
|
+
const models = this.getNodeParameter("models", i)
|
|
175
|
+
.split(",")
|
|
176
|
+
.map((model) => model.trim())
|
|
177
|
+
.filter(Boolean);
|
|
178
|
+
if (models.length === 0) {
|
|
179
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "At least one model is required", { itemIndex: i });
|
|
180
|
+
}
|
|
181
|
+
const body = {
|
|
182
|
+
models,
|
|
183
|
+
prompt: this.getNodeParameter("prompt", i),
|
|
184
|
+
entity: {
|
|
185
|
+
name: this.getNodeParameter("entityName", i),
|
|
186
|
+
url: this.getNodeParameter("entityUrl", i),
|
|
187
|
+
},
|
|
188
|
+
location: this.getNodeParameter("country", i) || "US",
|
|
189
|
+
};
|
|
190
|
+
const submit = (await transport_1.heebApiRequest.call(this, "POST", "/api/query", body));
|
|
191
|
+
if (!this.getNodeParameter("waitForCompletion", i)) {
|
|
192
|
+
returnData.push({ json: submit, pairedItem: { item: i } });
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const timeoutMs = this.getNodeParameter("timeout", i) * 1000;
|
|
196
|
+
const intervalMs = this.getNodeParameter("pollInterval", i) * 1000;
|
|
197
|
+
const result = await (0, poll_1.pollForResult)({
|
|
198
|
+
jobId: submit.job_id,
|
|
199
|
+
fetchOnce: () => transport_1.heebApiRequest.call(this, "GET", `/api/query/${encodeURIComponent(submit.job_id)}`),
|
|
200
|
+
timeoutMs,
|
|
201
|
+
intervalMs,
|
|
202
|
+
});
|
|
203
|
+
returnData.push({
|
|
204
|
+
json: { job_id: submit.job_id, ...result },
|
|
205
|
+
pairedItem: { item: i },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const jobId = this.getNodeParameter("jobId", i);
|
|
210
|
+
const result = (await transport_1.heebApiRequest.call(this, "GET", `/api/query/${encodeURIComponent(jobId)}`));
|
|
211
|
+
returnData.push({ json: result, pairedItem: { item: i } });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (this.continueOnFail()) {
|
|
216
|
+
returnData.push({
|
|
217
|
+
json: { error: error.message },
|
|
218
|
+
pairedItem: { item: i },
|
|
219
|
+
});
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
throw (0, errors_1.wrapHeebError)(this.getNode(), error, i);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return [returnData];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
exports.Heeb = Heeb;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractApiError = extractApiError;
|
|
4
|
+
exports.heebErrorMessage = heebErrorMessage;
|
|
5
|
+
exports.wrapHeebError = wrapHeebError;
|
|
6
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
7
|
+
const poll_1 = require("./poll");
|
|
8
|
+
function extractApiError(body) {
|
|
9
|
+
if (typeof body === "string") {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(body);
|
|
12
|
+
if (typeof parsed.error === "string")
|
|
13
|
+
return parsed.error;
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
return body;
|
|
17
|
+
}
|
|
18
|
+
if (body !== null && typeof body === "object") {
|
|
19
|
+
const value = body.error;
|
|
20
|
+
if (typeof value === "string")
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
function heebErrorMessage(statusCode, body) {
|
|
26
|
+
const apiError = extractApiError(body);
|
|
27
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
28
|
+
return {
|
|
29
|
+
message: "Heeb rejected the API key",
|
|
30
|
+
description: "The x-api-key is missing or invalid. Check the API key in the Heeb credential.",
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (statusCode === 400 && /insufficient balance/i.test(apiError)) {
|
|
34
|
+
return {
|
|
35
|
+
message: "Insufficient Heeb credits",
|
|
36
|
+
description: "The account does not have enough credits for this query. Top up at https://heeb.ai and try again.",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (statusCode === 400 && /no jobs? found/i.test(apiError)) {
|
|
40
|
+
return {
|
|
41
|
+
message: "Job not found",
|
|
42
|
+
description: "No job with that job_id exists for this account. Use the job_id returned by Submit Query.",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (statusCode === 400) {
|
|
46
|
+
return {
|
|
47
|
+
message: apiError || "Heeb rejected the request",
|
|
48
|
+
description: "The request was invalid. Check the prompt, entity, models and country fields.",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
message: apiError || "Heeb request failed",
|
|
53
|
+
description: apiError
|
|
54
|
+
? ""
|
|
55
|
+
: `Heeb returned an unexpected response${statusCode ? ` (HTTP ${statusCode})` : ""}.`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function pickStatus(error) {
|
|
59
|
+
const raw = error.httpCode ??
|
|
60
|
+
error.statusCode ??
|
|
61
|
+
error.response?.statusCode ??
|
|
62
|
+
error.cause?.statusCode;
|
|
63
|
+
const code = Number(raw);
|
|
64
|
+
return Number.isFinite(code) ? code : undefined;
|
|
65
|
+
}
|
|
66
|
+
function pickBody(error) {
|
|
67
|
+
return error.response?.body ?? error.cause?.error ?? error.error;
|
|
68
|
+
}
|
|
69
|
+
function wrapHeebError(node, error, itemIndex) {
|
|
70
|
+
if (error instanceof poll_1.HeebTimeoutError) {
|
|
71
|
+
return new n8n_workflow_1.NodeOperationError(node, error.message, {
|
|
72
|
+
description: "The job is still running. Increase the timeout, or fetch it later with the Get Result operation and the returned job_id.",
|
|
73
|
+
itemIndex,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const httpError = (error ?? {});
|
|
77
|
+
const info = heebErrorMessage(pickStatus(httpError), pickBody(httpError));
|
|
78
|
+
return new n8n_workflow_1.NodeApiError(node, httpError, {
|
|
79
|
+
message: info.message,
|
|
80
|
+
description: info.description,
|
|
81
|
+
itemIndex,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
viewBox="167.4864 183.648 48.44968 42.538776"
|
|
4
|
+
width="48.449814"
|
|
5
|
+
height="42.538776"
|
|
6
|
+
version="1.1"
|
|
7
|
+
id="svg1"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
10
|
+
<defs
|
|
11
|
+
id="defs1" />
|
|
12
|
+
<g
|
|
13
|
+
id="object-3"
|
|
14
|
+
transform="matrix(2.2988028,0,0,2.2988028,-217.53218,-238.52254)">
|
|
15
|
+
<path
|
|
16
|
+
d="m 185.797,183.648 h -15.536 c -2.139,0.015 -3.466,2.332 -2.397,4.185 l 7.772,12.974 c 1.085,1.792 3.685,1.795 4.775,0.005 l 7.765,-12.964 c 1.083,-1.85 -0.237,-4.179 -2.38,-4.2 z"
|
|
17
|
+
style="stroke-width:1"
|
|
18
|
+
id="object-1" />
|
|
19
|
+
</g>
|
|
20
|
+
</svg>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HeebTimeoutError = void 0;
|
|
4
|
+
exports.isPending = isPending;
|
|
5
|
+
exports.pollForResult = pollForResult;
|
|
6
|
+
class HeebTimeoutError extends Error {
|
|
7
|
+
constructor(jobId, timeoutMs) {
|
|
8
|
+
super(`Heeb job ${jobId} did not settle within ${Math.round(timeoutMs / 1000)}s`);
|
|
9
|
+
this.jobId = jobId;
|
|
10
|
+
this.timeoutMs = timeoutMs;
|
|
11
|
+
this.name = "HeebTimeoutError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.HeebTimeoutError = HeebTimeoutError;
|
|
15
|
+
function isPending(response) {
|
|
16
|
+
return (typeof response === "object" &&
|
|
17
|
+
response !== null &&
|
|
18
|
+
response.status === "pending");
|
|
19
|
+
}
|
|
20
|
+
async function pollForResult(options) {
|
|
21
|
+
const now = options.now ?? (() => Date.now());
|
|
22
|
+
const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
23
|
+
const start = now();
|
|
24
|
+
for (;;) {
|
|
25
|
+
const response = await options.fetchOnce();
|
|
26
|
+
if (!isPending(response)) {
|
|
27
|
+
return response;
|
|
28
|
+
}
|
|
29
|
+
if (now() - start >= options.timeoutMs) {
|
|
30
|
+
throw new HeebTimeoutError(options.jobId, options.timeoutMs);
|
|
31
|
+
}
|
|
32
|
+
await sleep(options.intervalMs);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_BASE_URL = void 0;
|
|
4
|
+
exports.heebApiRequest = heebApiRequest;
|
|
5
|
+
exports.DEFAULT_BASE_URL = "https://heeb.ai";
|
|
6
|
+
async function heebApiRequest(method, path, body) {
|
|
7
|
+
const credentials = await this.getCredentials("heebApi");
|
|
8
|
+
const baseUrl = String(credentials.baseUrl || exports.DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
9
|
+
const options = {
|
|
10
|
+
method,
|
|
11
|
+
url: `${baseUrl}${path}`,
|
|
12
|
+
json: true,
|
|
13
|
+
};
|
|
14
|
+
if (body !== undefined) {
|
|
15
|
+
options.body = body;
|
|
16
|
+
}
|
|
17
|
+
return await this.helpers.httpRequestWithAuthentication.call(this, "heebApi", options);
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heebai/n8n-nodes-heeb",
|
|
3
|
+
"version": "26.9.1",
|
|
4
|
+
"description": "n8n community node for the Heeb LLM Mentions API, which measures how a brand is referenced inside AI answers.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"heeb",
|
|
9
|
+
"geo",
|
|
10
|
+
"ai-seo",
|
|
11
|
+
"llm"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"homepage": "https://heeb.ai/integrations/n8n",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Heeb",
|
|
17
|
+
"email": "hello@heeb.ai"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"email": "hello@heeb.ai"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20.15"
|
|
24
|
+
},
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "rm -rf dist && tsc && cp nodes/heeb/heeb.svg dist/nodes/heeb/heeb.svg",
|
|
28
|
+
"dev": "tsc --watch",
|
|
29
|
+
"test": "bun run build && vitest run",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"n8n": {
|
|
36
|
+
"n8nNodesApiVersion": 1,
|
|
37
|
+
"credentials": [
|
|
38
|
+
"dist/credentials/HeebApi.credentials.js"
|
|
39
|
+
],
|
|
40
|
+
"nodes": [
|
|
41
|
+
"dist/nodes/heeb/Heeb.node.js"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"n8n-workflow": "*"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.19.0",
|
|
49
|
+
"n8n-workflow": "*",
|
|
50
|
+
"typescript": "^5.6.3",
|
|
51
|
+
"vitest": "^3.2.0"
|
|
52
|
+
}
|
|
53
|
+
}
|