node-autotask 0.17.0 → 0.19.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/ARCHITECTURE.md +25 -12
- package/CHANGELOG.md +574 -0
- package/MCP_TOOL_CATALOG.json +2 -2
- package/MCP_TOOL_MANIFEST.md +5 -5
- package/N8N_MCP_MANIFEST.json +2 -2
- package/N8N_MCP_MANIFEST.md +3 -3
- package/README.md +8 -4
- package/capabilities.json +1574 -1574
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-UV2PECAL.js → chunk-4YKV7ORF.js} +2 -2
- package/dist/{chunk-NQVD2XLH.cjs → chunk-7RDR5DSI.cjs} +393 -393
- package/dist/{chunk-EGR2ZLYM.js → chunk-D5RAQ4DE.js} +2 -2
- package/dist/{chunk-5ZGXWS4E.cjs → chunk-JM52ZU7T.cjs} +1573 -1573
- package/dist/{chunk-HC6YWPIY.js → chunk-LB2X4X4B.js} +116 -24
- package/dist/{chunk-6SP6GJJR.js → chunk-NDK7WYWA.js} +1573 -1573
- package/dist/{chunk-4RJG73GY.cjs → chunk-QDYSHAU6.cjs} +177 -85
- package/dist/{chunk-DQIZUZWP.cjs → chunk-TIM75K3G.cjs} +135 -135
- package/dist/{index-BAFdPxjG.d.ts → index-DLugNSr7.d.ts} +59 -21
- package/dist/{index-CjC0oWZX.d.cts → index-aZ3tCh1h.d.cts} +59 -21
- package/dist/index.cjs +26 -22
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -4
- package/dist/operations/index.cjs +178 -105
- package/dist/operations/index.d.cts +12 -3
- package/dist/operations/index.d.ts +12 -3
- package/dist/operations/index.js +77 -4
- package/dist/pool.cjs +8 -8
- package/dist/pool.d.cts +1 -1
- package/dist/pool.d.ts +1 -1
- package/dist/pool.js +4 -4
- package/dist/resources/index.cjs +4 -4
- package/dist/resources/index.d.cts +1 -1
- package/dist/resources/index.d.ts +1 -1
- package/dist/resources/index.js +3 -3
- package/docs/API.md +92 -26
- package/package.json +4 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CAPABILITIES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NDK7WYWA.js";
|
|
4
4
|
|
|
5
5
|
// src/auth.ts
|
|
6
6
|
var AUTH_HEADERS = {
|
|
@@ -377,7 +377,8 @@ function parseVendorErrors(body) {
|
|
|
377
377
|
var DETERMINISTIC_VENDOR_500_PHRASES = [
|
|
378
378
|
/exceeds maximum length/i,
|
|
379
379
|
/Missing Required Field/i,
|
|
380
|
-
/Value does not exist for the required field/i
|
|
380
|
+
/Value does not exist for the required field/i,
|
|
381
|
+
/Unable to find .+ in the .+ Entity\./i
|
|
381
382
|
];
|
|
382
383
|
function isDeterministicVendor500(entries) {
|
|
383
384
|
if (entries === void 0) return false;
|
|
@@ -1535,6 +1536,7 @@ var DEFAULT_TIMEOUT_MS = 3e4;
|
|
|
1535
1536
|
var DEFAULT_MAX_RETRIES = 3;
|
|
1536
1537
|
var DEFAULT_MAINTENANCE_MAX_WAIT_MS = 36e5;
|
|
1537
1538
|
var DEFAULT_MAINTENANCE_POLL_MS = 3e4;
|
|
1539
|
+
var MAINTENANCE_WINDOW_MAX_MS = 4 * 36e5;
|
|
1538
1540
|
var DEFAULT_MAX_CONCURRENT = 10;
|
|
1539
1541
|
var DEFAULT_MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
1540
1542
|
var DEFAULT_MAX_SCAN_RECORDS = 500;
|
|
@@ -14689,12 +14691,26 @@ function normalizeZoneBase(raw) {
|
|
|
14689
14691
|
if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
|
|
14690
14692
|
return `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
|
|
14691
14693
|
}
|
|
14692
|
-
function
|
|
14693
|
-
const queryPath = `${resourcePath}/query`;
|
|
14694
|
+
function buildQueryBody(params) {
|
|
14694
14695
|
const conditions = normalizedConditions(params.filter);
|
|
14695
14696
|
const filter = conditions === void 0 ? void 0 : buildFilter(conditions);
|
|
14696
|
-
const includeFields = params
|
|
14697
|
+
const includeFields = effectiveIncludeFields(params);
|
|
14698
|
+
const body = {};
|
|
14699
|
+
if (params.maxRecords !== void 0) body.MaxRecords = params.maxRecords;
|
|
14700
|
+
if (includeFields !== void 0) body.IncludeFields = includeFields;
|
|
14701
|
+
body.filter = filter === void 0 ? [] : filter.filter;
|
|
14702
|
+
return body;
|
|
14703
|
+
}
|
|
14704
|
+
function effectiveIncludeFields(params) {
|
|
14705
|
+
if (params.includeFields === void 0) return void 0;
|
|
14706
|
+
return params.includeFields.includes("id") ? params.includeFields : [...params.includeFields, "id"];
|
|
14707
|
+
}
|
|
14708
|
+
function buildQueryRequest(baseUrl, resourcePath, params) {
|
|
14709
|
+
const queryPath = `${resourcePath}/query`;
|
|
14697
14710
|
if (params.via === "get") {
|
|
14711
|
+
const conditions = normalizedConditions(params.filter);
|
|
14712
|
+
const filter = conditions === void 0 ? void 0 : buildFilter(conditions);
|
|
14713
|
+
const includeFields = effectiveIncludeFields(params);
|
|
14698
14714
|
const search = {};
|
|
14699
14715
|
if (filter !== void 0) search.filter = filter.filter;
|
|
14700
14716
|
if (params.maxRecords !== void 0) search.maxRecords = params.maxRecords;
|
|
@@ -14712,11 +14728,7 @@ function buildQueryRequest(baseUrl, resourcePath, params) {
|
|
|
14712
14728
|
}
|
|
14713
14729
|
return req;
|
|
14714
14730
|
}
|
|
14715
|
-
|
|
14716
|
-
if (params.maxRecords !== void 0) body.MaxRecords = params.maxRecords;
|
|
14717
|
-
if (includeFields !== void 0) body.IncludeFields = includeFields;
|
|
14718
|
-
body.filter = filter === void 0 ? [] : filter.filter;
|
|
14719
|
-
return { method: "POST", path: queryPath, body, idempotent: true };
|
|
14731
|
+
return { method: "POST", path: queryPath, body: buildQueryBody(params), idempotent: true };
|
|
14720
14732
|
}
|
|
14721
14733
|
var ambientSignal = new AsyncLocalStorage();
|
|
14722
14734
|
function runWithSignal(signal, fn) {
|
|
@@ -14768,9 +14780,21 @@ function backoffDelay(attempt) {
|
|
|
14768
14780
|
const exp = Math.min(BACKOFF_CAP_MS, BACKOFF_BASE_MS * 2 ** attempt);
|
|
14769
14781
|
return Math.floor(exp / 2 + Math.random() * (exp / 2));
|
|
14770
14782
|
}
|
|
14771
|
-
|
|
14772
|
-
|
|
14773
|
-
return
|
|
14783
|
+
var MAINTENANCE_PROBE_BYTES = 256;
|
|
14784
|
+
function maintenanceProbe(text) {
|
|
14785
|
+
return Buffer.from(text, "utf8").subarray(0, MAINTENANCE_PROBE_BYTES).toString("utf8");
|
|
14786
|
+
}
|
|
14787
|
+
function looksLikeMaintenanceText(text) {
|
|
14788
|
+
return /maintenance|temporarily unavailable|service unavailable|outage|downtime|down for/i.test(
|
|
14789
|
+
maintenanceProbe(text)
|
|
14790
|
+
);
|
|
14791
|
+
}
|
|
14792
|
+
function isHtmlLike(contentType, body) {
|
|
14793
|
+
if (contentType !== null && /text\/html|application\/xhtml/i.test(contentType)) return true;
|
|
14794
|
+
return body.trimStart().startsWith("<");
|
|
14795
|
+
}
|
|
14796
|
+
function maintenanceExhaustedAction(maxWaitMs, waits) {
|
|
14797
|
+
return `The zone stayed in a maintenance window for ${Math.round(maxWaitMs / 6e4)} min \u2014 the SDK waited and retried ${waits} time(s) and gave up. Re-run after the window (the observed median is about 2 h; the ceiling is 4 h \u2014 MAINTENANCE_WINDOW_MAX_MS), or raise maintenanceMaxWaitMs.`;
|
|
14774
14798
|
}
|
|
14775
14799
|
function parseRetryAfter(value) {
|
|
14776
14800
|
if (value === null || value.length === 0) return void 0;
|
|
@@ -15206,6 +15230,7 @@ var HttpClient = class {
|
|
|
15206
15230
|
const maintenancePollMs = opts.maintenancePollMs ?? this.maintenancePollMs;
|
|
15207
15231
|
let firstMaintenanceAt;
|
|
15208
15232
|
let maintenanceWaits = 0;
|
|
15233
|
+
let maintenanceBudgetSpent = false;
|
|
15209
15234
|
const started = Date.now();
|
|
15210
15235
|
let deadlineAt = started + timeoutMs;
|
|
15211
15236
|
const callerSignal = opts.signal ?? ambientSignal.getStore();
|
|
@@ -15426,12 +15451,13 @@ var HttpClient = class {
|
|
|
15426
15451
|
httpStatus: status,
|
|
15427
15452
|
suggestedAction: "The zone returned a non-JSON 2xx body; verify baseUrl points at the REST API."
|
|
15428
15453
|
});
|
|
15429
|
-
if (err2.code === "UNEXPECTED_CONTENT_TYPE" && maintenanceMaxWaitMs > 0 &&
|
|
15454
|
+
if (err2.code === "UNEXPECTED_CONTENT_TYPE" && maintenanceMaxWaitMs > 0 && looksLikeMaintenanceText(text)) {
|
|
15430
15455
|
const now = Date.now();
|
|
15431
15456
|
firstMaintenanceAt ??= now;
|
|
15432
15457
|
const remaining = maintenanceMaxWaitMs - (now - firstMaintenanceAt);
|
|
15433
15458
|
if (remaining > 0) {
|
|
15434
15459
|
const wait = Math.min(maintenancePollMs, remaining);
|
|
15460
|
+
if (wait >= remaining) maintenanceBudgetSpent = true;
|
|
15435
15461
|
maintenanceWaits += 1;
|
|
15436
15462
|
this.logger.warn?.(
|
|
15437
15463
|
`[autotask] maintenance window on ${method} ${opts.path}; waiting ${Math.round(wait / 1e3)}s (retry ${maintenanceWaits}, up to ${Math.round(maintenanceMaxWaitMs / 6e4)} min)`,
|
|
@@ -15442,6 +15468,7 @@ var HttpClient = class {
|
|
|
15442
15468
|
await pause(wait);
|
|
15443
15469
|
continue;
|
|
15444
15470
|
}
|
|
15471
|
+
maintenanceBudgetSpent = true;
|
|
15445
15472
|
}
|
|
15446
15473
|
if (err2.code === "UNEXPECTED_CONTENT_TYPE" && retriable5xx && attempt < maxRetries) {
|
|
15447
15474
|
const retryAfter = parseRetryAfter(res.headers.get("Retry-After"));
|
|
@@ -15456,17 +15483,27 @@ var HttpClient = class {
|
|
|
15456
15483
|
continue;
|
|
15457
15484
|
}
|
|
15458
15485
|
}
|
|
15459
|
-
|
|
15486
|
+
const pageSnippet = typeof err2.details?.bodySnippet === "string" ? err2.details.bodySnippet : "";
|
|
15487
|
+
if (maintenanceWaits > 0) {
|
|
15460
15488
|
err2.details = {
|
|
15461
15489
|
...err2.details ?? {},
|
|
15462
15490
|
maintenance: {
|
|
15463
15491
|
waits: maintenanceWaits,
|
|
15464
15492
|
waitedMs: Date.now() - (firstMaintenanceAt ?? Date.now()),
|
|
15465
15493
|
maxWaitMs: maintenanceMaxWaitMs,
|
|
15466
|
-
exhausted:
|
|
15494
|
+
exhausted: maintenanceBudgetSpent
|
|
15467
15495
|
}
|
|
15468
15496
|
};
|
|
15469
|
-
|
|
15497
|
+
if (maintenanceBudgetSpent) {
|
|
15498
|
+
err2.suggestedAction = maintenanceExhaustedAction(maintenanceMaxWaitMs, maintenanceWaits);
|
|
15499
|
+
}
|
|
15500
|
+
}
|
|
15501
|
+
if (err2.code === "UNEXPECTED_CONTENT_TYPE" && !looksLikeMaintenanceText(text) && isHtmlLike(res.headers.get("Content-Type"), text)) {
|
|
15502
|
+
err2.details = { ...err2.details ?? {}, maintenancePatternMiss: true };
|
|
15503
|
+
this.logger.debug?.(
|
|
15504
|
+
`[autotask] non-JSON body did not match the maintenance vocabulary on ${method} ${opts.path}: ${pageSnippet}`,
|
|
15505
|
+
correlationId
|
|
15506
|
+
);
|
|
15470
15507
|
}
|
|
15471
15508
|
attachEmptyReasonDetails(err2);
|
|
15472
15509
|
throw err2;
|
|
@@ -15478,12 +15515,37 @@ var HttpClient = class {
|
|
|
15478
15515
|
break;
|
|
15479
15516
|
}
|
|
15480
15517
|
let body;
|
|
15518
|
+
let nonJsonBody;
|
|
15481
15519
|
if (text.length > 0) {
|
|
15482
15520
|
try {
|
|
15483
15521
|
body = JSON.parse(text);
|
|
15484
15522
|
} catch {
|
|
15485
15523
|
body = text;
|
|
15524
|
+
nonJsonBody = text;
|
|
15525
|
+
}
|
|
15526
|
+
}
|
|
15527
|
+
const nonJsonSnippet = nonJsonBody === void 0 ? void 0 : bodySnippet(nonJsonBody, this.redactPersonalData);
|
|
15528
|
+
const nonJsonIsMaintenance = nonJsonBody !== void 0 && looksLikeMaintenanceText(nonJsonBody);
|
|
15529
|
+
const nonJsonIsHtmlCandidate = nonJsonBody !== void 0 && isHtmlLike(res.headers.get("Content-Type"), nonJsonBody);
|
|
15530
|
+
const nonJsonMaintenanceMayWait = nonJsonIsMaintenance && retriable5xx;
|
|
15531
|
+
if (nonJsonBody !== void 0 && maintenanceMaxWaitMs > 0 && nonJsonMaintenanceMayWait) {
|
|
15532
|
+
const now = Date.now();
|
|
15533
|
+
firstMaintenanceAt ??= now;
|
|
15534
|
+
const remaining = maintenanceMaxWaitMs - (now - firstMaintenanceAt);
|
|
15535
|
+
if (remaining > 0) {
|
|
15536
|
+
const wait = Math.min(maintenancePollMs, remaining);
|
|
15537
|
+
if (wait >= remaining) maintenanceBudgetSpent = true;
|
|
15538
|
+
maintenanceWaits += 1;
|
|
15539
|
+
this.logger.warn?.(
|
|
15540
|
+
`[autotask] maintenance window on ${method} ${opts.path} (HTTP ${status}); waiting ${Math.round(wait / 1e3)}s (retry ${maintenanceWaits}, up to ${Math.round(maintenanceMaxWaitMs / 6e4)} min)`,
|
|
15541
|
+
correlationId
|
|
15542
|
+
);
|
|
15543
|
+
deadlineAt = Date.now() + wait + timeoutMs;
|
|
15544
|
+
armDeadline(deadlineAt - Date.now());
|
|
15545
|
+
await pause(wait);
|
|
15546
|
+
continue;
|
|
15486
15547
|
}
|
|
15548
|
+
maintenanceBudgetSpent = true;
|
|
15487
15549
|
}
|
|
15488
15550
|
const deterministic500 = status >= 500 && isDeterministicVendor500(parseVendorErrors(body));
|
|
15489
15551
|
const retryableStatus = status === 429 || status >= 500;
|
|
@@ -15526,6 +15588,32 @@ var HttpClient = class {
|
|
|
15526
15588
|
credentialShapeExpectation(headers, this.auth)
|
|
15527
15589
|
);
|
|
15528
15590
|
err.correlationId = correlationId;
|
|
15591
|
+
if (maintenanceWaits > 0) {
|
|
15592
|
+
err.details = {
|
|
15593
|
+
...err.details ?? {},
|
|
15594
|
+
maintenance: {
|
|
15595
|
+
waits: maintenanceWaits,
|
|
15596
|
+
waitedMs: Date.now() - (firstMaintenanceAt ?? Date.now()),
|
|
15597
|
+
maxWaitMs: maintenanceMaxWaitMs,
|
|
15598
|
+
exhausted: maintenanceBudgetSpent
|
|
15599
|
+
}
|
|
15600
|
+
};
|
|
15601
|
+
if (maintenanceBudgetSpent) {
|
|
15602
|
+
err.suggestedAction = maintenanceExhaustedAction(maintenanceMaxWaitMs, maintenanceWaits);
|
|
15603
|
+
}
|
|
15604
|
+
}
|
|
15605
|
+
if (nonJsonBody !== void 0) {
|
|
15606
|
+
if (nonJsonIsMaintenance && !retriable5xx) {
|
|
15607
|
+
err.details = { ...err.details ?? {}, maintenanceDetected: true };
|
|
15608
|
+
err.suggestedAction = `A maintenance-shaped page answered ${method} ${opts.path} (HTTP ${status}); it was NOT waited out or re-sent \u2014 a non-2xx edge page can arrive after the request was forwarded, so verify the record before re-issuing (only a GET or an explicitly idempotent-flagged call waits this window out).`;
|
|
15609
|
+
} else if (!nonJsonIsMaintenance && nonJsonIsHtmlCandidate) {
|
|
15610
|
+
err.details = { ...err.details ?? {}, maintenancePatternMiss: true };
|
|
15611
|
+
this.logger.debug?.(
|
|
15612
|
+
`[autotask] non-JSON body did not match the maintenance vocabulary on ${method} ${opts.path}: ${nonJsonSnippet}`,
|
|
15613
|
+
correlationId
|
|
15614
|
+
);
|
|
15615
|
+
}
|
|
15616
|
+
}
|
|
15529
15617
|
if (status === 401 && zoneRetry !== void 0) {
|
|
15530
15618
|
err.details = {
|
|
15531
15619
|
...err.details ?? {},
|
|
@@ -15982,6 +16070,7 @@ function applyFieldsFilter(projected, filter, row) {
|
|
|
15982
16070
|
if (filter !== void 0) {
|
|
15983
16071
|
const present = new Set(projected.map((f) => f.name));
|
|
15984
16072
|
row.unmatchedFields = filter.filter((n) => !present.has(n));
|
|
16073
|
+
row.projected = true;
|
|
15985
16074
|
}
|
|
15986
16075
|
row.fields = kept;
|
|
15987
16076
|
row.required = kept.filter((f) => f.isRequired).map((f) => f.name);
|
|
@@ -17042,7 +17131,7 @@ var BaseResource = class {
|
|
|
17042
17131
|
// ------------------------------------------------------------------
|
|
17043
17132
|
// Query walk (wired to the G3 walker; cursor-follow per the pinned wire)
|
|
17044
17133
|
// ------------------------------------------------------------------
|
|
17045
|
-
/** One page fetch: page 1 POSTs the QueryModel body; later pages POST the
|
|
17134
|
+
/** One page fetch: page 1 POSTs the QueryModel body; later pages POST the same body (`buildQueryBody`) to the absolute cursor (issue #118). */
|
|
17046
17135
|
async fetchPage(params, page, pageSize, state) {
|
|
17047
17136
|
const operation = `${this.config.key}.query`;
|
|
17048
17137
|
if (page === 1) {
|
|
@@ -17058,8 +17147,7 @@ var BaseResource = class {
|
|
|
17058
17147
|
operation
|
|
17059
17148
|
});
|
|
17060
17149
|
}
|
|
17061
|
-
const
|
|
17062
|
-
const body = { filter: conditions === void 0 ? [] : buildFilter(conditions).filter };
|
|
17150
|
+
const body = buildQueryBody({ ...params, maxRecords: pageSize });
|
|
17063
17151
|
const res = await this.http.request({
|
|
17064
17152
|
method: "POST",
|
|
17065
17153
|
path: cursorToPath(this.http.baseUrl, cursor),
|
|
@@ -17112,10 +17200,12 @@ var BaseResource = class {
|
|
|
17112
17200
|
* complete-enough result. The client never owns the walk:
|
|
17113
17201
|
* - `limit` = total records (default 500, hard max 2500 — above it throws
|
|
17114
17202
|
* AutotaskConfigError; it is NOT a page size). The page-1 request uses
|
|
17115
|
-
* the internal page size min(500, limit)
|
|
17116
|
-
*
|
|
17117
|
-
*
|
|
17118
|
-
*
|
|
17203
|
+
* the internal page size min(500, limit) (the `latest` mechanism sizes
|
|
17204
|
+
* its walk from the matching count); every follow-up page POSTs the
|
|
17205
|
+
* SAME QueryModel body (`buildQueryBody`: MaxRecords, IncludeFields and
|
|
17206
|
+
* the preserved filter) to the absolute cursor (issue #118 — the vendor
|
|
17207
|
+
* honours IncludeFields there and ignores MaxRecords, serving its own
|
|
17208
|
+
* page size), so the walk reuses `fetchPage` (the shared cursor machinery)
|
|
17119
17209
|
* with a per-walk closure state (walks never share state).
|
|
17120
17210
|
* - The 1000-page guard still applies as a SOFT stop: a walk that would
|
|
17121
17211
|
* fetch a 1001st page stops with `hasMore: true` (never a throw — this
|
|
@@ -18689,6 +18779,7 @@ export {
|
|
|
18689
18779
|
DEFAULT_MAX_RETRIES,
|
|
18690
18780
|
DEFAULT_MAINTENANCE_MAX_WAIT_MS,
|
|
18691
18781
|
DEFAULT_MAINTENANCE_POLL_MS,
|
|
18782
|
+
MAINTENANCE_WINDOW_MAX_MS,
|
|
18692
18783
|
DEFAULT_MAX_CONCURRENT,
|
|
18693
18784
|
DEFAULT_MAX_RESPONSE_BYTES,
|
|
18694
18785
|
DEFAULT_MAX_SCAN_RECORDS,
|
|
@@ -18734,6 +18825,7 @@ export {
|
|
|
18734
18825
|
IDEMPOTENT_METHODS,
|
|
18735
18826
|
buildUrl,
|
|
18736
18827
|
cursorToPath,
|
|
18828
|
+
buildQueryBody,
|
|
18737
18829
|
buildQueryRequest,
|
|
18738
18830
|
runWithSignal,
|
|
18739
18831
|
HttpClient,
|