n8n-nodes-alterlab 0.11.0 → 0.12.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/dist/credentials/AlterLabApi.credentials.d.ts.map +1 -1
- package/dist/credentials/AlterLabApi.credentials.js +4 -2
- package/dist/credentials/AlterLabApi.credentials.js.map +1 -1
- package/dist/nodes/AlterLab/AlterLab.node.d.ts.map +1 -1
- package/dist/nodes/AlterLab/AlterLab.node.js +192 -58
- package/dist/nodes/AlterLab/AlterLab.node.js.map +1 -1
- package/dist/nodes/AlterLab/url.d.ts +23 -0
- package/dist/nodes/AlterLab/url.d.ts.map +1 -0
- package/dist/nodes/AlterLab/url.js +245 -0
- package/dist/nodes/AlterLab/url.js.map +1 -0
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlterLabApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/AlterLabApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,eAAe,EAChB,MAAM,cAAc,CAAC;AAEtB,qBAAa,WAAY,YAAW,eAAe;IACjD,IAAI,SAAiB;IACrB,WAAW,SAAkB;IAC7B,gBAAgB,SACmF;IAEnG,UAAU,EAAE,eAAe,EAAE,CAmB3B;IAEF,YAAY,EAAE,oBAAoB,CAOhC;IAEF,IAAI,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"AlterLabApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/AlterLabApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,eAAe,EAChB,MAAM,cAAc,CAAC;AAEtB,qBAAa,WAAY,YAAW,eAAe;IACjD,IAAI,SAAiB;IACrB,WAAW,SAAkB;IAC7B,gBAAgB,SACmF;IAEnG,UAAU,EAAE,eAAe,EAAE,CAmB3B;IAEF,YAAY,EAAE,oBAAoB,CAOhC;IAEF,IAAI,EAAE,sBAAsB,CAU1B;CACH"}
|
|
@@ -34,8 +34,10 @@ class AlterLabApi {
|
|
|
34
34
|
};
|
|
35
35
|
this.test = {
|
|
36
36
|
request: {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Keep exactly one trailing slash so n8n resolves the relative URL as a
|
|
38
|
+
// path reference instead of replacing a self-hosted path prefix.
|
|
39
|
+
baseURL: "={{($credentials.baseUrl || 'https://api.alterlab.io').replace(/\\/+$/, '') + '/'}}",
|
|
40
|
+
url: "={{($credentials.baseUrl || 'https://api.alterlab.io').replace(/\\/+$/, '').endsWith('/api/v1') ? 'usage' : 'api/v1/usage'}}",
|
|
39
41
|
method: "GET",
|
|
40
42
|
},
|
|
41
43
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlterLabApi.credentials.js","sourceRoot":"","sources":["../../credentials/AlterLabApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,WAAW;IAAxB;QACE,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GAAG,cAAc,CAAC;QAC7B,qBAAgB,GACd,gGAAgG,CAAC;QAEnG,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,EACT,uYAAuY;aAC1Y;YACD;gBACE,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,yBAAyB;gBAClC,WAAW,EACT,+DAA+D;aAClE;SACF,CAAC;QAEF,iBAAY,GAAyB;YACnC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,WAAW,EAAE,0BAA0B;iBACxC;aACF;SACF,CAAC;QAEF,SAAI,GAA2B;YAC7B,OAAO,EAAE;gBACP,OAAO,
|
|
1
|
+
{"version":3,"file":"AlterLabApi.credentials.js","sourceRoot":"","sources":["../../credentials/AlterLabApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,WAAW;IAAxB;QACE,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GAAG,cAAc,CAAC;QAC7B,qBAAgB,GACd,gGAAgG,CAAC;QAEnG,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,EACT,uYAAuY;aAC1Y;YACD;gBACE,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,yBAAyB;gBAClC,WAAW,EACT,+DAA+D;aAClE;SACF,CAAC;QAEF,iBAAY,GAAyB;YACnC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,WAAW,EAAE,0BAA0B;iBACxC;aACF;SACF,CAAC;QAEF,SAAI,GAA2B;YAC7B,OAAO,EAAE;gBACP,wEAAwE;gBACxE,iEAAiE;gBACjE,OAAO,EACL,qFAAqF;gBACvF,GAAG,EACD,8HAA8H;gBAChI,MAAM,EAAE,KAAK;aACd;SACF,CAAC;IACJ,CAAC;CAAA;AA/CD,kCA+CC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlterLab.node.d.ts","sourceRoot":"","sources":["../../../nodes/AlterLab/AlterLab.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAErB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"AlterLab.node.d.ts","sourceRoot":"","sources":["../../../nodes/AlterLab/AlterLab.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAErB,MAAM,cAAc,CAAC;AAUtB,qBAAa,QAAS,YAAW,SAAS;IACxC,WAAW,EAAE,oBAAoB,CAksD/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAshDxE"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AlterLab = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const url_1 = require("./url");
|
|
5
6
|
const UTM = "utm_source=n8n&utm_medium=integration&utm_campaign=community_node";
|
|
6
7
|
class AlterLab {
|
|
7
8
|
constructor() {
|
|
@@ -471,6 +472,8 @@ class AlterLab {
|
|
|
471
472
|
{ name: "FAQ", value: "faq" },
|
|
472
473
|
{ name: "Recipe", value: "recipe" },
|
|
473
474
|
{ name: "Event", value: "event" },
|
|
475
|
+
{ name: "Ecommerce Homepage", value: "ecommerce_homepage" },
|
|
476
|
+
{ name: "Directory Listing", value: "directory_listing" },
|
|
474
477
|
],
|
|
475
478
|
description: "Pre-defined extraction profile for structured data",
|
|
476
479
|
},
|
|
@@ -499,6 +502,20 @@ class AlterLab {
|
|
|
499
502
|
placeholder: "gpt-4o",
|
|
500
503
|
description: "LLM model override for this request in provider-specific format (e.g. gpt-4o, claude-opus-4-5-20251101, llama3-70b-8192). Overrides the model configured on your BYOK key for this request only.",
|
|
501
504
|
},
|
|
505
|
+
{
|
|
506
|
+
displayName: "Extraction Provider",
|
|
507
|
+
name: "extractionProvider",
|
|
508
|
+
type: "options",
|
|
509
|
+
default: "",
|
|
510
|
+
options: [
|
|
511
|
+
{ name: "Auto (use most recent BYOK key)", value: "" },
|
|
512
|
+
{ name: "OpenAI", value: "openai" },
|
|
513
|
+
{ name: "Anthropic", value: "anthropic" },
|
|
514
|
+
{ name: "OpenRouter", value: "openrouter" },
|
|
515
|
+
{ name: "Groq", value: "groq" },
|
|
516
|
+
],
|
|
517
|
+
description: "LLM provider to use for extraction. Selects the matching BYOK key registered at your AlterLab dashboard. When omitted, the most recently used registered key is used automatically.",
|
|
518
|
+
},
|
|
502
519
|
{
|
|
503
520
|
displayName: "Promote Schema.org",
|
|
504
521
|
name: "promoteSchemaOrg",
|
|
@@ -813,6 +830,20 @@ class AlterLab {
|
|
|
813
830
|
placeholder: "gpt-4o",
|
|
814
831
|
description: "LLM model override for each crawled page in provider-specific format (e.g. gpt-4o, claude-opus-4-5-20251101, llama3-70b-8192). Overrides the model configured on your BYOK key.",
|
|
815
832
|
},
|
|
833
|
+
{
|
|
834
|
+
displayName: "Extraction Provider",
|
|
835
|
+
name: "extractionProvider",
|
|
836
|
+
type: "options",
|
|
837
|
+
default: "",
|
|
838
|
+
options: [
|
|
839
|
+
{ name: "Auto (use most recent BYOK key)", value: "" },
|
|
840
|
+
{ name: "OpenAI", value: "openai" },
|
|
841
|
+
{ name: "Anthropic", value: "anthropic" },
|
|
842
|
+
{ name: "OpenRouter", value: "openrouter" },
|
|
843
|
+
{ name: "Groq", value: "groq" },
|
|
844
|
+
],
|
|
845
|
+
description: "LLM provider to use for extraction on each crawled page. Selects the matching BYOK key registered at your AlterLab dashboard.",
|
|
846
|
+
},
|
|
816
847
|
],
|
|
817
848
|
},
|
|
818
849
|
// ══════════════════════════════════════════════════════
|
|
@@ -1074,7 +1105,7 @@ class AlterLab {
|
|
|
1074
1105
|
default: "",
|
|
1075
1106
|
required: true,
|
|
1076
1107
|
placeholder: "<html>...</html> or raw text / markdown",
|
|
1077
|
-
description: "Raw content to extract from (HTML, text, or markdown — max
|
|
1108
|
+
description: "Raw content to extract from (HTML, text, or markdown — max 500,000 characters)",
|
|
1078
1109
|
displayOptions: {
|
|
1079
1110
|
show: {
|
|
1080
1111
|
resource: ["extract"],
|
|
@@ -1142,6 +1173,8 @@ class AlterLab {
|
|
|
1142
1173
|
{ name: "None", value: "" },
|
|
1143
1174
|
{ name: "Auto", value: "auto" },
|
|
1144
1175
|
{ name: "Article", value: "article" },
|
|
1176
|
+
{ name: "Directory Listing", value: "directory_listing" },
|
|
1177
|
+
{ name: "Ecommerce Homepage", value: "ecommerce_homepage" },
|
|
1145
1178
|
{ name: "Event", value: "event" },
|
|
1146
1179
|
{ name: "FAQ", value: "faq" },
|
|
1147
1180
|
{ name: "Job Posting", value: "job_posting" },
|
|
@@ -1150,6 +1183,25 @@ class AlterLab {
|
|
|
1150
1183
|
],
|
|
1151
1184
|
description: "Pre-defined extraction profile (schema template)",
|
|
1152
1185
|
},
|
|
1186
|
+
{
|
|
1187
|
+
displayName: "Extraction Template",
|
|
1188
|
+
name: "extractionTemplate",
|
|
1189
|
+
type: "options",
|
|
1190
|
+
default: "",
|
|
1191
|
+
options: [
|
|
1192
|
+
{ name: "None", value: "" },
|
|
1193
|
+
{ name: "Auto", value: "auto" },
|
|
1194
|
+
{ name: "Article", value: "article" },
|
|
1195
|
+
{ name: "Directory Listing", value: "directory_listing" },
|
|
1196
|
+
{ name: "Ecommerce Homepage", value: "ecommerce_homepage" },
|
|
1197
|
+
{ name: "Event", value: "event" },
|
|
1198
|
+
{ name: "FAQ", value: "faq" },
|
|
1199
|
+
{ name: "Job Posting", value: "job_posting" },
|
|
1200
|
+
{ name: "Product", value: "product" },
|
|
1201
|
+
{ name: "Recipe", value: "recipe" },
|
|
1202
|
+
],
|
|
1203
|
+
description: "Shorthand alias for Extraction Profile. Mutually exclusive with Extraction Profile — use one or the other.",
|
|
1204
|
+
},
|
|
1153
1205
|
{
|
|
1154
1206
|
displayName: "Extraction Prompt",
|
|
1155
1207
|
name: "extractionPrompt",
|
|
@@ -1167,6 +1219,20 @@ class AlterLab {
|
|
|
1167
1219
|
placeholder: "gpt-4o",
|
|
1168
1220
|
description: "LLM model override for this request in provider-specific format (e.g. gpt-4o, claude-opus-4-5-20251101, llama3-70b-8192). Overrides the model configured on your BYOK key for this request only.",
|
|
1169
1221
|
},
|
|
1222
|
+
{
|
|
1223
|
+
displayName: "Extraction Provider",
|
|
1224
|
+
name: "extractionProvider",
|
|
1225
|
+
type: "options",
|
|
1226
|
+
default: "",
|
|
1227
|
+
options: [
|
|
1228
|
+
{ name: "Auto (use most recent BYOK key)", value: "" },
|
|
1229
|
+
{ name: "OpenAI", value: "openai" },
|
|
1230
|
+
{ name: "Anthropic", value: "anthropic" },
|
|
1231
|
+
{ name: "OpenRouter", value: "openrouter" },
|
|
1232
|
+
{ name: "Groq", value: "groq" },
|
|
1233
|
+
],
|
|
1234
|
+
description: "LLM provider to use for extraction. Selects the matching BYOK key registered at your AlterLab dashboard.",
|
|
1235
|
+
},
|
|
1170
1236
|
{
|
|
1171
1237
|
displayName: "Source URL",
|
|
1172
1238
|
name: "sourceUrl",
|
|
@@ -1182,6 +1248,38 @@ class AlterLab {
|
|
|
1182
1248
|
default: false,
|
|
1183
1249
|
description: "Whether to include provenance/evidence for extracted fields",
|
|
1184
1250
|
},
|
|
1251
|
+
{
|
|
1252
|
+
displayName: "Cache Control",
|
|
1253
|
+
name: "cache",
|
|
1254
|
+
type: "options",
|
|
1255
|
+
default: "auto",
|
|
1256
|
+
options: [
|
|
1257
|
+
{
|
|
1258
|
+
name: "Auto (return cached if available)",
|
|
1259
|
+
value: "auto",
|
|
1260
|
+
description: "Return cached LLM extraction result if available, otherwise call the LLM and cache the result",
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
name: "Skip (bypass cache, always call LLM)",
|
|
1264
|
+
value: "skip",
|
|
1265
|
+
description: "Bypass cache lookup and always invoke the LLM. Result is still stored for future callers.",
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
name: "Only (cached result or 404)",
|
|
1269
|
+
value: "only",
|
|
1270
|
+
description: "Return cached result only. Returns HTTP 404 if the result is not cached — never calls the LLM.",
|
|
1271
|
+
},
|
|
1272
|
+
],
|
|
1273
|
+
description: "Cache control for LLM extraction results. auto: use cache if available (default). skip: always call LLM. only: return cached or 404.",
|
|
1274
|
+
},
|
|
1275
|
+
{
|
|
1276
|
+
displayName: "Cache TTL (Seconds)",
|
|
1277
|
+
name: "cacheTtl",
|
|
1278
|
+
type: "number",
|
|
1279
|
+
default: 3600,
|
|
1280
|
+
typeOptions: { minValue: 1, maxValue: 86400 },
|
|
1281
|
+
description: "How long to cache this extraction result in seconds (1–86400). Defaults to 3600s (1 hour). Max 86400s (24 hours).",
|
|
1282
|
+
},
|
|
1185
1283
|
],
|
|
1186
1284
|
},
|
|
1187
1285
|
// ══════════════════════════════════════════════════════
|
|
@@ -1535,7 +1633,7 @@ class AlterLab {
|
|
|
1535
1633
|
};
|
|
1536
1634
|
}
|
|
1537
1635
|
async execute() {
|
|
1538
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27;
|
|
1636
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29;
|
|
1539
1637
|
const items = this.getInputData();
|
|
1540
1638
|
const results = [];
|
|
1541
1639
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -1543,13 +1641,17 @@ class AlterLab {
|
|
|
1543
1641
|
const resource = this.getNodeParameter("resource", i);
|
|
1544
1642
|
// ── Detect credential type ────────────────────────
|
|
1545
1643
|
let authName = "alterLabApi";
|
|
1644
|
+
let credentials;
|
|
1546
1645
|
try {
|
|
1547
|
-
await this.getCredentials("alterLabOAuth2Api");
|
|
1646
|
+
credentials = await this.getCredentials("alterLabOAuth2Api");
|
|
1548
1647
|
authName = "alterLabOAuth2Api";
|
|
1549
1648
|
}
|
|
1550
1649
|
catch {
|
|
1551
1650
|
// OAuth2 not configured, fall back to API key
|
|
1651
|
+
credentials = await this.getCredentials("alterLabApi");
|
|
1552
1652
|
}
|
|
1653
|
+
const apiBaseUrl = (0, url_1.normalizeBaseUrl)(credentials.baseUrl);
|
|
1654
|
+
const apiUrl = (endpoint) => (0, url_1.resolveApiUrl)(apiBaseUrl, endpoint);
|
|
1553
1655
|
// ══════════════════════════════════════════════════
|
|
1554
1656
|
// SESSION RESOURCE
|
|
1555
1657
|
// ══════════════════════════════════════════════════
|
|
@@ -1593,7 +1695,7 @@ class AlterLab {
|
|
|
1593
1695
|
body.expires_at = extra.expiresAt;
|
|
1594
1696
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1595
1697
|
method: "POST",
|
|
1596
|
-
url: "/api/v1/sessions",
|
|
1698
|
+
url: apiUrl("/api/v1/sessions"),
|
|
1597
1699
|
body,
|
|
1598
1700
|
json: true,
|
|
1599
1701
|
returnFullResponse: true,
|
|
@@ -1617,7 +1719,7 @@ class AlterLab {
|
|
|
1617
1719
|
qs.status = filters.status;
|
|
1618
1720
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1619
1721
|
method: "GET",
|
|
1620
|
-
url: "/api/v1/sessions",
|
|
1722
|
+
url: apiUrl("/api/v1/sessions"),
|
|
1621
1723
|
qs,
|
|
1622
1724
|
json: true,
|
|
1623
1725
|
returnFullResponse: true,
|
|
@@ -1646,7 +1748,7 @@ class AlterLab {
|
|
|
1646
1748
|
const sessionId = this.getNodeParameter("sessionId", i);
|
|
1647
1749
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1648
1750
|
method: "GET",
|
|
1649
|
-
url: `/api/v1/sessions/${sessionId}
|
|
1751
|
+
url: apiUrl(`/api/v1/sessions/${sessionId}`),
|
|
1650
1752
|
json: true,
|
|
1651
1753
|
returnFullResponse: true,
|
|
1652
1754
|
ignoreHttpStatusErrors: true,
|
|
@@ -1694,7 +1796,7 @@ class AlterLab {
|
|
|
1694
1796
|
}
|
|
1695
1797
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1696
1798
|
method: "PATCH",
|
|
1697
|
-
url: `/api/v1/sessions/${sessionId}
|
|
1799
|
+
url: apiUrl(`/api/v1/sessions/${sessionId}`),
|
|
1698
1800
|
body,
|
|
1699
1801
|
json: true,
|
|
1700
1802
|
returnFullResponse: true,
|
|
@@ -1713,7 +1815,7 @@ class AlterLab {
|
|
|
1713
1815
|
const sessionId = this.getNodeParameter("sessionId", i);
|
|
1714
1816
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1715
1817
|
method: "DELETE",
|
|
1716
|
-
url: `/api/v1/sessions/${sessionId}
|
|
1818
|
+
url: apiUrl(`/api/v1/sessions/${sessionId}`),
|
|
1717
1819
|
json: true,
|
|
1718
1820
|
returnFullResponse: true,
|
|
1719
1821
|
ignoreHttpStatusErrors: true,
|
|
@@ -1732,7 +1834,7 @@ class AlterLab {
|
|
|
1732
1834
|
const sessionId = this.getNodeParameter("sessionId", i);
|
|
1733
1835
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1734
1836
|
method: "POST",
|
|
1735
|
-
url: `/api/v1/sessions/${sessionId}/validate
|
|
1837
|
+
url: apiUrl(`/api/v1/sessions/${sessionId}/validate`),
|
|
1736
1838
|
json: true,
|
|
1737
1839
|
returnFullResponse: true,
|
|
1738
1840
|
ignoreHttpStatusErrors: true,
|
|
@@ -1787,7 +1889,7 @@ class AlterLab {
|
|
|
1787
1889
|
}
|
|
1788
1890
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1789
1891
|
method: "POST",
|
|
1790
|
-
url: `/api/v1/sessions/${sessionId}/refresh
|
|
1892
|
+
url: apiUrl(`/api/v1/sessions/${sessionId}/refresh`),
|
|
1791
1893
|
body,
|
|
1792
1894
|
json: true,
|
|
1793
1895
|
returnFullResponse: true,
|
|
@@ -1813,7 +1915,7 @@ class AlterLab {
|
|
|
1813
1915
|
const crawlId = this.getNodeParameter("crawlId", i);
|
|
1814
1916
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1815
1917
|
method: "GET",
|
|
1816
|
-
url: `/api/v1/crawl/${crawlId}
|
|
1918
|
+
url: apiUrl(`/api/v1/crawl/${crawlId}`),
|
|
1817
1919
|
json: true,
|
|
1818
1920
|
returnFullResponse: true,
|
|
1819
1921
|
ignoreHttpStatusErrors: true,
|
|
@@ -1831,7 +1933,7 @@ class AlterLab {
|
|
|
1831
1933
|
const crawlId = this.getNodeParameter("crawlId", i);
|
|
1832
1934
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1833
1935
|
method: "DELETE",
|
|
1834
|
-
url: `/api/v1/crawl/${crawlId}
|
|
1936
|
+
url: apiUrl(`/api/v1/crawl/${crawlId}`),
|
|
1835
1937
|
json: true,
|
|
1836
1938
|
returnFullResponse: true,
|
|
1837
1939
|
ignoreHttpStatusErrors: true,
|
|
@@ -1873,6 +1975,9 @@ class AlterLab {
|
|
|
1873
1975
|
if (settings.extractionModel) {
|
|
1874
1976
|
body.extraction_model = settings.extractionModel;
|
|
1875
1977
|
}
|
|
1978
|
+
if (settings.extractionProvider) {
|
|
1979
|
+
body.extraction_provider = settings.extractionProvider;
|
|
1980
|
+
}
|
|
1876
1981
|
const advanced = {};
|
|
1877
1982
|
if (settings.renderJs && settings.renderJs !== "false") {
|
|
1878
1983
|
advanced.render_js = settings.renderJs === "auto" ? "auto" : true;
|
|
@@ -1891,9 +1996,10 @@ class AlterLab {
|
|
|
1891
1996
|
if (Object.keys(advanced).length > 0)
|
|
1892
1997
|
body.advanced = advanced;
|
|
1893
1998
|
// Submit the crawl
|
|
1999
|
+
const crawlRequestUrl = apiUrl("/api/v1/crawl");
|
|
1894
2000
|
const startResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1895
2001
|
method: "POST",
|
|
1896
|
-
url:
|
|
2002
|
+
url: crawlRequestUrl,
|
|
1897
2003
|
body,
|
|
1898
2004
|
json: true,
|
|
1899
2005
|
returnFullResponse: true,
|
|
@@ -1916,6 +2022,7 @@ class AlterLab {
|
|
|
1916
2022
|
let pollDelay = 2000;
|
|
1917
2023
|
const maxPollDelay = 10000;
|
|
1918
2024
|
let finalBody = startBody;
|
|
2025
|
+
let crawlPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, startResponse, `/api/v1/crawl/${crawlId}`, crawlRequestUrl);
|
|
1919
2026
|
while (Date.now() - pollStart < pollTimeoutMs) {
|
|
1920
2027
|
await new Promise((resolve) => {
|
|
1921
2028
|
setTimeout(resolve, pollDelay);
|
|
@@ -1923,11 +2030,12 @@ class AlterLab {
|
|
|
1923
2030
|
pollDelay = Math.min(pollDelay * 1.5, maxPollDelay);
|
|
1924
2031
|
const pollResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1925
2032
|
method: "GET",
|
|
1926
|
-
url:
|
|
2033
|
+
url: crawlPollUrl,
|
|
1927
2034
|
json: true,
|
|
1928
2035
|
returnFullResponse: true,
|
|
1929
2036
|
ignoreHttpStatusErrors: true,
|
|
1930
2037
|
});
|
|
2038
|
+
crawlPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, pollResponse, crawlPollUrl, crawlPollUrl);
|
|
1931
2039
|
const pollStatus = pollResponse
|
|
1932
2040
|
.statusCode;
|
|
1933
2041
|
const pollBody = pollResponse.body;
|
|
@@ -1991,9 +2099,10 @@ class AlterLab {
|
|
|
1991
2099
|
}
|
|
1992
2100
|
}
|
|
1993
2101
|
}
|
|
2102
|
+
const searchRequestUrl = apiUrl("/api/v1/search");
|
|
1994
2103
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
1995
2104
|
method: "POST",
|
|
1996
|
-
url:
|
|
2105
|
+
url: searchRequestUrl,
|
|
1997
2106
|
body,
|
|
1998
2107
|
json: true,
|
|
1999
2108
|
returnFullResponse: true,
|
|
@@ -2015,6 +2124,7 @@ class AlterLab {
|
|
|
2015
2124
|
const searchPollStart = Date.now();
|
|
2016
2125
|
const maxSearchPollMs = 120000; // 2 min max
|
|
2017
2126
|
let finalData = data;
|
|
2127
|
+
let searchPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, response, `/api/v1/search/${searchId}`, searchRequestUrl);
|
|
2018
2128
|
while (Date.now() - searchPollStart < maxSearchPollMs) {
|
|
2019
2129
|
await new Promise((resolve) => {
|
|
2020
2130
|
setTimeout(resolve, searchPollDelay);
|
|
@@ -2022,11 +2132,12 @@ class AlterLab {
|
|
|
2022
2132
|
searchPollDelay = Math.min(searchPollDelay * 1.5, maxSearchPollDelay);
|
|
2023
2133
|
const pollResp = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
2024
2134
|
method: "GET",
|
|
2025
|
-
url:
|
|
2135
|
+
url: searchPollUrl,
|
|
2026
2136
|
json: true,
|
|
2027
2137
|
returnFullResponse: true,
|
|
2028
2138
|
ignoreHttpStatusErrors: true,
|
|
2029
2139
|
});
|
|
2140
|
+
searchPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, pollResp, searchPollUrl, searchPollUrl);
|
|
2030
2141
|
const pollStatus = pollResp
|
|
2031
2142
|
.statusCode;
|
|
2032
2143
|
const pollBody = pollResp
|
|
@@ -2082,7 +2193,7 @@ class AlterLab {
|
|
|
2082
2193
|
body.sitemap_path = opts.sitemapPath;
|
|
2083
2194
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
2084
2195
|
method: "POST",
|
|
2085
|
-
url: "/api/v1/map",
|
|
2196
|
+
url: apiUrl("/api/v1/map"),
|
|
2086
2197
|
body,
|
|
2087
2198
|
json: true,
|
|
2088
2199
|
returnFullResponse: true,
|
|
@@ -2123,17 +2234,25 @@ class AlterLab {
|
|
|
2123
2234
|
}
|
|
2124
2235
|
if (opts.extractionProfile)
|
|
2125
2236
|
body.extraction_profile = opts.extractionProfile;
|
|
2237
|
+
if (opts.extractionTemplate)
|
|
2238
|
+
body.extraction_template = opts.extractionTemplate;
|
|
2126
2239
|
if (opts.extractionPrompt)
|
|
2127
2240
|
body.extraction_prompt = opts.extractionPrompt;
|
|
2128
2241
|
if (opts.extractionModel)
|
|
2129
2242
|
body.extraction_model = opts.extractionModel;
|
|
2243
|
+
if (opts.extractionProvider)
|
|
2244
|
+
body.extraction_provider = opts.extractionProvider;
|
|
2130
2245
|
if (opts.sourceUrl)
|
|
2131
2246
|
body.source_url = opts.sourceUrl;
|
|
2132
2247
|
if (opts.evidence)
|
|
2133
2248
|
body.evidence = true;
|
|
2249
|
+
if (opts.cache && opts.cache !== "auto")
|
|
2250
|
+
body.cache = opts.cache;
|
|
2251
|
+
if (opts.cacheTtl && opts.cacheTtl !== 3600)
|
|
2252
|
+
body.cache_ttl = opts.cacheTtl;
|
|
2134
2253
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
2135
2254
|
method: "POST",
|
|
2136
|
-
url: "/api/v1/extract",
|
|
2255
|
+
url: apiUrl("/api/v1/extract"),
|
|
2137
2256
|
body,
|
|
2138
2257
|
json: true,
|
|
2139
2258
|
returnFullResponse: true,
|
|
@@ -2152,8 +2271,14 @@ class AlterLab {
|
|
|
2152
2271
|
creditsUsed: (_p = data.credits_used) !== null && _p !== void 0 ? _p : 0,
|
|
2153
2272
|
modelUsed: (_q = data.model_used) !== null && _q !== void 0 ? _q : null,
|
|
2154
2273
|
extractionMethod: (_r = data.extraction_method) !== null && _r !== void 0 ? _r : "algorithmic",
|
|
2155
|
-
|
|
2274
|
+
extractionProfile: (_s = data.extraction_profile) !== null && _s !== void 0 ? _s : null,
|
|
2275
|
+
contentSizeChars: (_t = data.content_size_chars) !== null && _t !== void 0 ? _t : 0,
|
|
2276
|
+
cacheHit: (_u = data.cache_hit) !== null && _u !== void 0 ? _u : false,
|
|
2156
2277
|
};
|
|
2278
|
+
if (data.extraction_metadata !== undefined &&
|
|
2279
|
+
data.extraction_metadata !== null) {
|
|
2280
|
+
output.extractionMetadata = data.extraction_metadata;
|
|
2281
|
+
}
|
|
2157
2282
|
// Flatten format results into top-level keys
|
|
2158
2283
|
if (formats && typeof formats === "object") {
|
|
2159
2284
|
if (formats.text !== undefined)
|
|
@@ -2191,9 +2316,10 @@ class AlterLab {
|
|
|
2191
2316
|
const body = { urls };
|
|
2192
2317
|
if (opts.webhookUrl)
|
|
2193
2318
|
body.webhook_url = opts.webhookUrl;
|
|
2319
|
+
const batchRequestUrl = apiUrl("/api/v1/batch");
|
|
2194
2320
|
const submitResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
2195
2321
|
method: "POST",
|
|
2196
|
-
url:
|
|
2322
|
+
url: batchRequestUrl,
|
|
2197
2323
|
body,
|
|
2198
2324
|
json: true,
|
|
2199
2325
|
returnFullResponse: true,
|
|
@@ -2212,11 +2338,12 @@ class AlterLab {
|
|
|
2212
2338
|
continue;
|
|
2213
2339
|
}
|
|
2214
2340
|
// Poll until complete or timeout
|
|
2215
|
-
const batchPollTimeoutMs = ((
|
|
2341
|
+
const batchPollTimeoutMs = ((_v = opts.pollTimeout) !== null && _v !== void 0 ? _v : 300) * 1000;
|
|
2216
2342
|
const batchPollStart = Date.now();
|
|
2217
2343
|
let batchDelay = 1000;
|
|
2218
2344
|
const maxBatchDelay = 8000;
|
|
2219
2345
|
let finalBatchBody = submitBody;
|
|
2346
|
+
let batchPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, submitResponse, `/api/v1/batch/${batchId}`, batchRequestUrl);
|
|
2220
2347
|
while (Date.now() - batchPollStart < batchPollTimeoutMs) {
|
|
2221
2348
|
await new Promise((resolve) => {
|
|
2222
2349
|
setTimeout(resolve, batchDelay);
|
|
@@ -2224,11 +2351,12 @@ class AlterLab {
|
|
|
2224
2351
|
batchDelay = Math.min(batchDelay * 1.5, maxBatchDelay);
|
|
2225
2352
|
const pollResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
2226
2353
|
method: "GET",
|
|
2227
|
-
url:
|
|
2354
|
+
url: batchPollUrl,
|
|
2228
2355
|
json: true,
|
|
2229
2356
|
returnFullResponse: true,
|
|
2230
2357
|
ignoreHttpStatusErrors: true,
|
|
2231
2358
|
});
|
|
2359
|
+
batchPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, pollResponse, batchPollUrl, batchPollUrl);
|
|
2232
2360
|
const pollStatus = pollResponse
|
|
2233
2361
|
.statusCode;
|
|
2234
2362
|
const pollBody = pollResponse
|
|
@@ -2289,7 +2417,7 @@ class AlterLab {
|
|
|
2289
2417
|
}
|
|
2290
2418
|
const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
2291
2419
|
method: "POST",
|
|
2292
|
-
url: "/api/v1/scrape/estimate",
|
|
2420
|
+
url: apiUrl("/api/v1/scrape/estimate"),
|
|
2293
2421
|
body,
|
|
2294
2422
|
json: true,
|
|
2295
2423
|
returnFullResponse: true,
|
|
@@ -2304,12 +2432,12 @@ class AlterLab {
|
|
|
2304
2432
|
const data = responseBody;
|
|
2305
2433
|
results.push({
|
|
2306
2434
|
json: {
|
|
2307
|
-
url: (
|
|
2308
|
-
estimatedTier: (
|
|
2309
|
-
estimatedCredits: (
|
|
2310
|
-
confidence: (
|
|
2311
|
-
maxPossibleCredits: (
|
|
2312
|
-
reasoning: (
|
|
2435
|
+
url: (_w = data.url) !== null && _w !== void 0 ? _w : "",
|
|
2436
|
+
estimatedTier: (_x = data.estimated_tier) !== null && _x !== void 0 ? _x : "unknown",
|
|
2437
|
+
estimatedCredits: (_y = data.estimated_credits) !== null && _y !== void 0 ? _y : 0,
|
|
2438
|
+
confidence: (_z = data.confidence) !== null && _z !== void 0 ? _z : "low",
|
|
2439
|
+
maxPossibleCredits: (_0 = data.max_possible_credits) !== null && _0 !== void 0 ? _0 : 0,
|
|
2440
|
+
reasoning: (_1 = data.reasoning) !== null && _1 !== void 0 ? _1 : "",
|
|
2313
2441
|
},
|
|
2314
2442
|
});
|
|
2315
2443
|
continue;
|
|
@@ -2336,7 +2464,7 @@ class AlterLab {
|
|
|
2336
2464
|
body.body = requestBody;
|
|
2337
2465
|
}
|
|
2338
2466
|
// Output options
|
|
2339
|
-
if ((
|
|
2467
|
+
if ((_2 = outputOptions.formats) === null || _2 === void 0 ? void 0 : _2.length) {
|
|
2340
2468
|
body.formats = outputOptions.formats;
|
|
2341
2469
|
}
|
|
2342
2470
|
if (outputOptions.includeRawHtml) {
|
|
@@ -2438,6 +2566,9 @@ class AlterLab {
|
|
|
2438
2566
|
if (extraction.extractionModel) {
|
|
2439
2567
|
body.extraction_model = extraction.extractionModel;
|
|
2440
2568
|
}
|
|
2569
|
+
if (extraction.extractionProvider) {
|
|
2570
|
+
body.extraction_provider = extraction.extractionProvider;
|
|
2571
|
+
}
|
|
2441
2572
|
// Cost controls → nested "cost_controls" object
|
|
2442
2573
|
const costCtrl = {};
|
|
2443
2574
|
if (costControls.maxCredits && costControls.maxCredits > 0) {
|
|
@@ -2459,9 +2590,10 @@ class AlterLab {
|
|
|
2459
2590
|
body.cost_controls = costCtrl;
|
|
2460
2591
|
}
|
|
2461
2592
|
// ── Make the API call ─────────────────────────────
|
|
2593
|
+
const scrapeRequestUrl = apiUrl("/api/v1/scrape");
|
|
2462
2594
|
let response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
2463
2595
|
method: "POST",
|
|
2464
|
-
url:
|
|
2596
|
+
url: scrapeRequestUrl,
|
|
2465
2597
|
body,
|
|
2466
2598
|
json: true,
|
|
2467
2599
|
returnFullResponse: true,
|
|
@@ -2474,18 +2606,20 @@ class AlterLab {
|
|
|
2474
2606
|
const jobId = responseBody.job_id;
|
|
2475
2607
|
let delay = 500;
|
|
2476
2608
|
const maxDelay = 5000;
|
|
2477
|
-
const maxPollTime = (((
|
|
2609
|
+
const maxPollTime = (((_3 = outputOptions.timeout) !== null && _3 !== void 0 ? _3 : 90) + 30) * 1000; // timeout + 30s buffer
|
|
2478
2610
|
const pollStart = Date.now();
|
|
2611
|
+
let scrapePollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, response, `/api/v1/jobs/${jobId}`, scrapeRequestUrl);
|
|
2479
2612
|
while (Date.now() - pollStart < maxPollTime) {
|
|
2480
2613
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2481
2614
|
delay = Math.min(delay * 2, maxDelay);
|
|
2482
2615
|
const pollResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
|
|
2483
2616
|
method: "GET",
|
|
2484
|
-
url:
|
|
2617
|
+
url: scrapePollUrl,
|
|
2485
2618
|
json: true,
|
|
2486
2619
|
returnFullResponse: true,
|
|
2487
2620
|
ignoreHttpStatusErrors: true,
|
|
2488
2621
|
});
|
|
2622
|
+
scrapePollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, pollResponse, scrapePollUrl, scrapePollUrl);
|
|
2489
2623
|
const pollStatus = pollResponse
|
|
2490
2624
|
.statusCode;
|
|
2491
2625
|
const pollBody = pollResponse
|
|
@@ -2514,14 +2648,14 @@ class AlterLab {
|
|
|
2514
2648
|
const data = responseBody;
|
|
2515
2649
|
const content = data.content;
|
|
2516
2650
|
const output = {
|
|
2517
|
-
url: (
|
|
2518
|
-
statusCode: (
|
|
2519
|
-
title: (
|
|
2520
|
-
author: (
|
|
2521
|
-
publishedAt: (
|
|
2522
|
-
cached: (
|
|
2523
|
-
responseTimeMs: (
|
|
2524
|
-
sizeBytes: (
|
|
2651
|
+
url: (_4 = data.url) !== null && _4 !== void 0 ? _4 : "",
|
|
2652
|
+
statusCode: (_5 = data.status_code) !== null && _5 !== void 0 ? _5 : 0,
|
|
2653
|
+
title: (_6 = data.title) !== null && _6 !== void 0 ? _6 : null,
|
|
2654
|
+
author: (_7 = data.author) !== null && _7 !== void 0 ? _7 : null,
|
|
2655
|
+
publishedAt: (_8 = data.published_at) !== null && _8 !== void 0 ? _8 : null,
|
|
2656
|
+
cached: (_9 = data.cached) !== null && _9 !== void 0 ? _9 : false,
|
|
2657
|
+
responseTimeMs: (_10 = data.response_time_ms) !== null && _10 !== void 0 ? _10 : 0,
|
|
2658
|
+
sizeBytes: (_11 = data.size_bytes) !== null && _11 !== void 0 ? _11 : 0,
|
|
2525
2659
|
};
|
|
2526
2660
|
// Flatten multi-format content.
|
|
2527
2661
|
// When extraction_schema is provided, the API overrides content.json
|
|
@@ -2529,32 +2663,32 @@ class AlterLab {
|
|
|
2529
2663
|
// therefore carries the extraction result in that case.
|
|
2530
2664
|
if (content && typeof content === "object") {
|
|
2531
2665
|
output.markdown =
|
|
2532
|
-
(
|
|
2533
|
-
output.text = (
|
|
2534
|
-
output.json = (
|
|
2535
|
-
output.jsonV2 = (
|
|
2536
|
-
output.rag = (
|
|
2537
|
-
output.html = (
|
|
2666
|
+
(_12 = content.markdown) !== null && _12 !== void 0 ? _12 : null;
|
|
2667
|
+
output.text = (_13 = content.text) !== null && _13 !== void 0 ? _13 : null;
|
|
2668
|
+
output.json = (_14 = content.json) !== null && _14 !== void 0 ? _14 : null;
|
|
2669
|
+
output.jsonV2 = (_15 = content.json_v2) !== null && _15 !== void 0 ? _15 : null;
|
|
2670
|
+
output.rag = (_16 = content.rag) !== null && _16 !== void 0 ? _16 : null;
|
|
2671
|
+
output.html = (_17 = content.html) !== null && _17 !== void 0 ? _17 : null;
|
|
2538
2672
|
}
|
|
2539
2673
|
else {
|
|
2540
2674
|
output.markdown = content !== null && content !== void 0 ? content : null;
|
|
2541
2675
|
}
|
|
2542
2676
|
// Extraction results — top-level filtered_content is still returned
|
|
2543
2677
|
// by the API alongside the content.json override above.
|
|
2544
|
-
output.filteredContent = (
|
|
2545
|
-
output.extractionMethod = (
|
|
2678
|
+
output.filteredContent = (_18 = data.filtered_content) !== null && _18 !== void 0 ? _18 : null;
|
|
2679
|
+
output.extractionMethod = (_19 = data.extraction_method) !== null && _19 !== void 0 ? _19 : null;
|
|
2546
2680
|
// Advanced outputs
|
|
2547
|
-
output.screenshotUrl = (
|
|
2548
|
-
output.pdfUrl = (
|
|
2549
|
-
output.ocrResults = (
|
|
2550
|
-
output.rawHtml = (
|
|
2681
|
+
output.screenshotUrl = (_20 = data.screenshot_url) !== null && _20 !== void 0 ? _20 : null;
|
|
2682
|
+
output.pdfUrl = (_21 = data.pdf_url) !== null && _21 !== void 0 ? _21 : null;
|
|
2683
|
+
output.ocrResults = (_22 = data.ocr_results) !== null && _22 !== void 0 ? _22 : null;
|
|
2684
|
+
output.rawHtml = (_23 = data.raw_html) !== null && _23 !== void 0 ? _23 : null;
|
|
2551
2685
|
// Billing breakdown (flattened)
|
|
2552
2686
|
const billing = data.billing;
|
|
2553
2687
|
output.billing = {
|
|
2554
|
-
cost: (
|
|
2555
|
-
tier: (
|
|
2556
|
-
savings: (
|
|
2557
|
-
suggestion: (
|
|
2688
|
+
cost: (_25 = (_24 = billing === null || billing === void 0 ? void 0 : billing.total_credits) !== null && _24 !== void 0 ? _24 : data.credits_used) !== null && _25 !== void 0 ? _25 : 0,
|
|
2689
|
+
tier: (_27 = (_26 = billing === null || billing === void 0 ? void 0 : billing.tier_used) !== null && _26 !== void 0 ? _26 : data.tier_used) !== null && _27 !== void 0 ? _27 : "unknown",
|
|
2690
|
+
savings: (_28 = billing === null || billing === void 0 ? void 0 : billing.savings) !== null && _28 !== void 0 ? _28 : 0,
|
|
2691
|
+
suggestion: (_29 = billing === null || billing === void 0 ? void 0 : billing.optimization_suggestion) !== null && _29 !== void 0 ? _29 : null,
|
|
2558
2692
|
};
|
|
2559
2693
|
results.push({ json: output });
|
|
2560
2694
|
}
|
|
@@ -2611,7 +2745,7 @@ function handleApiError(ctx, statusCode, body, itemIndex) {
|
|
|
2611
2745
|
case 402:
|
|
2612
2746
|
throw new n8n_workflow_1.NodeApiError(ctx.getNode(), body, {
|
|
2613
2747
|
message: "Insufficient balance",
|
|
2614
|
-
description:
|
|
2748
|
+
description: `Top up your balance at https://app.alterlab.io/dashboard/billing?${UTM}`,
|
|
2615
2749
|
httpCode: "402",
|
|
2616
2750
|
itemIndex,
|
|
2617
2751
|
});
|