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
package/docs/API.md
CHANGED
|
@@ -93,7 +93,7 @@ auto-retry on all legs — plus a concurrency guard (`maxConcurrent`, default 10
|
|
|
93
93
|
allows <= 10 concurrent), one correlation id per request (surfaced on every
|
|
94
94
|
error and to `onAudit`), and the opt-in client-side hourly budget tracker
|
|
95
95
|
(`rateLimit.perHour`, default off — a sliding one-hour window that waits for a slot within the call deadline, else throws `RateLimitError` with `retryAfter`; the API allows 10,000 req/hour). The tracker is PER PROCESS: it counts only this client's attempts and cannot see other instances or integrations on the tenant (`thresholdInformation.get()` is the only cross-integration signal; the local count is `client.hourlyBudget()`, section 3.2.1). Under the `agent` config profile the exhaustion `suggestedAction` says stop or narrow the query — never raise the limit.
|
|
96
|
-
Redirects are never followed (a 3xx throws `REDIRECT_BLOCKED`, so auth headers cannot leave the configured origin). Retry waits are capped at 60 s and never outlast the call deadline: a larger `Retry-After` (or one that exceeds the remaining budget) throws the `RateLimitError` immediately. `RequestOptions` adds `signal` (caller `AbortSignal`, combined with the deadline; aborting throws `AutotaskAbortError`, code `ABORTED`, and cancels retry sleeps) and `idempotent` (retry a non-GET method on 5xx/network errors; set on the `/query` and `/query/count` POST reads; on a mutation it is the issue #105 opt-back into the pre-#105 auto-retry — never set on real writes by the SDK). `client.withSignal(signal, () => ...)` applies a caller `AbortSignal` to every request (and retry wait) made inside the callback — the way to propagate MCP request cancellation through `invoke` without a signal parameter on each method. A real write clears the read cache. A non-JSON 2xx body is typed by the response `Content-Type` (section 2.1): a non-JSON-ish header throws the retryable `UNEXPECTED_CONTENT_TYPE` (the edge/maintenance page — a maintenance notice is waited out up to `maintenanceMaxWaitMs`, default 1 h, for reads and writes; otherwise a GET or idempotent-flagged call retries and a write makes one call
|
|
96
|
+
Redirects are never followed (a 3xx throws `REDIRECT_BLOCKED`, so auth headers cannot leave the configured origin). Retry waits are capped at 60 s and never outlast the call deadline: a larger `Retry-After` (or one that exceeds the remaining budget) throws the `RateLimitError` immediately. `RequestOptions` adds `signal` (caller `AbortSignal`, combined with the deadline; aborting throws `AutotaskAbortError`, code `ABORTED`, and cancels retry sleeps) and `idempotent` (retry a non-GET method on 5xx/network errors; set on the `/query` and `/query/count` POST reads; on a mutation it is the issue #105 opt-back into the pre-#105 auto-retry — never set on real writes by the SDK). `client.withSignal(signal, () => ...)` applies a caller `AbortSignal` to every request (and retry wait) made inside the callback — the way to propagate MCP request cancellation through `invoke` without a signal parameter on each method. A real write clears the read cache. A non-JSON 2xx body is typed by the response `Content-Type` (section 2.1): a non-JSON-ish header throws the retryable `UNEXPECTED_CONTENT_TYPE` (the edge/maintenance page — a maintenance notice is waited out up to `maintenanceMaxWaitMs`, default 1 h, for reads and writes; the same wait recognises the page at any status for calls safe to re-send (a GET or an explicitly idempotent-flagged call — an unflagged mutation makes ONE send with `details.maintenanceDetected`): section 2.1; otherwise a GET or idempotent-flagged call retries and a write makes one call); a JSON-ish or ABSENT header keeps `INVALID_RESPONSE` (the #13/W-A7 shape-drift key); JSON under any other header still decodes (the decode attempt is unconditional). A blank 2xx on a call that expects a result (every method but `DELETE` by default — `RequestOptions.expectBody`) throws the typed `EMPTY_RESPONSE` (`DELETE` and 204 keep returning `undefined`). A response above `maxResponseBytes` throws `RESPONSE_TOO_LARGE`; HTTP 412 maps to `ConflictError` (category `conflict`).
|
|
97
97
|
Per-tenant 429 cooldown gate (`cooldown`, ON by default): once a 429 surfaces from the transport (retries spent, a `Retry-After` too long to wait out, or a non-retriable method), the client records a cooldown deadline for that tenant (lowercased `UserName` + lowercased host — impersonation does not change the key), and every further call for that tenant fails LOCALLY — before the semaphore, the hourly limiter and fetch, with zero upstream calls — with a `RateLimitError` (`code: 'RATE_LIMITED_COOLDOWN'`, `notSent: true`, `retryAfter` = remaining seconds; no credentials appear in the message) until the deadline passes. Duration is `Retry-After` clamped to `cooldown.maxMs` (default 15 minutes), or `cooldown.defaultMs` (default 60 s) when the header is missing or unparseable; the thread-threshold 429 (vendor code `Itgenatr005`, which clears in seconds) arms only a short window: min(`Retry-After`, 10 s), default 5 s. A 2xx from a call that got through clears the gate early; expiry is lazy (the first call after the deadline deletes the entry and proceeds); a hostile `Retry-After` (huge, zero, negative, non-numeric) is clamped to the cap or falls back to the default, so a lock-out is always bounded. `thresholdInformation.get()` bypasses the gate so a throttled tenant can still read its own headroom (a 2xx from it clears the gate; a 429 re-arms it). The default store is the in-process in-memory map shared by every client instance (capped at 1024 entries, evicting the earliest deadline first — failing open); `cooldown: { enabled: false }` disables the gate and `cooldown.store` injects a custom `CooldownStore` (the interface leaves room for a shared/Redis-backed store). Gated calls emit an audit event marked `notSent` and do not count toward `rateLimit.perHour`. Limits worth knowing: the gate is per process — other processes and integrations on the same tenant are invisible to it (`thresholdInformation` is the only cross-integration signal); it can mask early recovery if the vendor window clears before `Retry-After`; a process that loads both the ESM and the CJS build gets two default stores (the default store is deliberately not anchored on `globalThis`) — inject one shared `CooldownStore` if you need cross-build sharing.
|
|
98
98
|
|
|
99
99
|
Credential-shaped fields are redacted in audit payloads by default.
|
|
@@ -166,8 +166,10 @@ served payload verbatim).
|
|
|
166
166
|
|
|
167
167
|
`buildFilter(conditions)` wraps caller conditions (multiple top-level
|
|
168
168
|
conditions are `and`-wrapped); `validateFilter` enforces the vendor caps
|
|
169
|
-
(section 5 below). `
|
|
170
|
-
(`{ MaxRecords, IncludeFields?, filter }
|
|
169
|
+
(section 5 below). `buildQueryBody` produces the `QueryModel` wire body
|
|
170
|
+
(`{ MaxRecords, IncludeFields?, filter }`, `id` appended to a caller
|
|
171
|
+
`includeFields`), shared by page 1 (`buildQueryRequest`) and every cursor
|
|
172
|
+
continuation of a walk (issue #118). `normalizedConditions` is the
|
|
171
173
|
shared filter-shape gate of the query family (every query-family entry
|
|
172
174
|
point routes through it pre-request): the `filter` param accepts a
|
|
173
175
|
conditions array or `{ filter: [...] }`, and `undefined`/`null` are the
|
|
@@ -257,7 +259,7 @@ status → what the vendor does that triggers it.
|
|
|
257
259
|
| `RateLimitError` | `RATE_LIMITED` | rate_limit | 429 | hourly/secondly budget exceeded — `retryAfter` from `Retry-After` — retryable |
|
|
258
260
|
| `RateLimitError` | `RATE_LIMITED_COOLDOWN` | rate_limit | — (call not sent) | per-tenant cooldown after a surfaced 429 (issue #6) — `notSent: true`, `retryAfter` = remaining seconds — not retryable before the deadline |
|
|
259
261
|
| `ServerError` | `SERVER_ERROR` | server | 5xx | vendor 5xx — retryable — EXCEPT a deterministic field-validation 500 (a known phrase, `isDeterministicVendor500`): same class/code/status, `retryable: false` + `details.deterministic: true` (issue #23) |
|
|
260
|
-
| `UnexpectedContentTypeError` | `UNEXPECTED_CONTENT_TYPE` | server | 2xx (non-JSON body + a non-JSON-ish `Content-Type`) | the edge serves a non-JSON page (maintenance) — the vendor never processed the request; `details = { httpStatus, contentType, bodySnippet (256 B) }` — retryable (advisory). A
|
|
262
|
+
| `UnexpectedContentTypeError` | `UNEXPECTED_CONTENT_TYPE` | server | 2xx (non-JSON body + a non-JSON-ish `Content-Type`) | the edge serves a non-JSON page (maintenance) — the vendor never processed the request; `details = { httpStatus, contentType, bodySnippet (256 B) }` — retryable (advisory). A non-JSON `2xx` maintenance page is waited out up to `maintenanceMaxWaitMs` (default 1 h) for reads AND writes; at `4xx`/`5xx` the page is waited out only for calls safe to re-send (GET / explicitly idempotent-flagged) — an unflagged mutation makes one send and carries `details.maintenanceDetected` (issue #114); a wait that ran is reported on the surfaced error as `details.maintenance` (section 2.1) |
|
|
261
263
|
| `EmptyResponseError` | `EMPTY_RESPONSE` | server | 2xx (empty body, `expectBody`) | a blank 2xx on a call that expects a result (non-DELETE by default — section 2.1); `details = { httpStatus, method }` — retryable for non-DELETE (advisory: the loop never retries 2xx) |
|
|
262
264
|
| `CircuitBreakerError` | `CIRCUIT_BREAKER_OPEN` | policy | — (call not sent) | the host circuit breaker (issue #26, opt-in) is open — refused LOCALLY before the request (zero upstream calls, no hourly stamp) — `notSent: true`, `retryAfter` = remaining open seconds — **retryable: true** (the deliberate divergence from #6's not-retryable cooldown refusal: the open period is bounded and advertised; section 3.2.2) |
|
|
263
265
|
| `ResolutionError` | `RESOLUTION_AMBIGUOUS` / `RESOLUTION_TRUNCATED` / `NOT_FOUND` | resolution | — | `resolve`: multiple exact matches (candidates in `resourceIds`, labeled in `details.idRefs`); a cap-tripped bounded client scan (`resolution.scanOnMiss` only; never a silent `null`); a numeric-id miss |
|
|
@@ -340,21 +342,41 @@ transport `RequestOptions` flag) or raises `maxRetries`. An
|
|
|
340
342
|
contract break is not transient.
|
|
341
343
|
|
|
342
344
|
**Maintenance-window auto-retry (weekly window).** Autotask runs a weekly
|
|
343
|
-
maintenance window
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
`
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
a
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
345
|
+
maintenance window during which the edge answers an HTML page before the
|
|
346
|
+
application ever sees the request. The transport recognises that page from the
|
|
347
|
+
**first 256 bytes** of its body at any status (`2xx`, `4xx`, `5xx`) — issue
|
|
348
|
+
#114: the edge picks the status line, and a 503/404 HTML page used to burn the
|
|
349
|
+
retry budget or surface as `NOT_FOUND`. The gate is the body: a **JSON** body
|
|
350
|
+
never enters (a JSON 503 is the application answering; it keeps the normal
|
|
351
|
+
rules, and a write still makes one send). When the page reads as a maintenance
|
|
352
|
+
notice:
|
|
353
|
+
|
|
354
|
+
- a non-JSON **`2xx`** page is waited out and retried for **reads and writes**
|
|
355
|
+
(a 2xx HTML page is edge-served before execution — the 0.16.0 premise);
|
|
356
|
+
- a non-JSON **`4xx`/`5xx`** page is waited out and retried only for calls that
|
|
357
|
+
are **safe to re-send** (`retriable5xx`: a `GET` or an explicitly
|
|
358
|
+
`idempotent`-flagged call). An UNFLAGGED mutation answered with such a page
|
|
359
|
+
makes **exactly one send** and surfaces the status's own error with
|
|
360
|
+
`details.maintenanceDetected = true` and a verify-before-reissuing action — a
|
|
361
|
+
proxy can serve that page AFTER forwarding the write, so the outcome is
|
|
362
|
+
unknown (the §2.2 write rule is intact).
|
|
363
|
+
|
|
364
|
+
The wait polls `maintenancePollMs` (default 30 s) for up to
|
|
365
|
+
`maintenanceMaxWaitMs` (default 1 hour) from the first such response. The
|
|
366
|
+
observed ceiling is 4 h (`MAINTENANCE_WINDOW_MAX_MS`; 19 live windows:
|
|
367
|
+
30–240 min, median ≈ 2 h) — an unattended worker should pass
|
|
368
|
+
`maintenanceMaxWaitMs: MAINTENANCE_WINDOW_MAX_MS`. The wait does not consume
|
|
369
|
+
`maxRetries`, each post-wait attempt gets a fresh `timeoutMs`, and a caller
|
|
370
|
+
`AbortSignal` interrupts the wait. A non-JSON HTML-ish page that does not look
|
|
371
|
+
like a maintenance notice (for example a wrong `baseUrl` serving a web site, or
|
|
372
|
+
a stock proxy page) is surfaced at once with
|
|
373
|
+
`details.maintenancePatternMiss = true` and the 256-byte snippet logged at
|
|
374
|
+
`debug` (issue #114 — the next occurrence supplies the wording). When a wait
|
|
375
|
+
ran, the surfaced error always carries
|
|
376
|
+
`details.maintenance = { waits, waitedMs, maxWaitMs, exhausted }` (`exhausted`
|
|
377
|
+
is true when the budget was spent; a wording change on a later page surfaces
|
|
378
|
+
that page's own error with `exhausted: false`). Disable with
|
|
379
|
+
`maintenanceMaxWaitMs: 0` (config or per call).
|
|
358
380
|
Zone discovery rides the same transport, so a startup failure during
|
|
359
381
|
maintenance is recognisable by the code. `details.bodySnippet` is the first
|
|
360
382
|
256 BYTES of the raw body — TRUNCATION ONLY (the name-based redaction is a
|
|
@@ -386,7 +408,10 @@ write that answered nothing is now a typed failure (strict default: no
|
|
|
386
408
|
|
|
387
409
|
**Deterministic validation 500 (#23).** A 500 whose `errors[]` entries match
|
|
388
410
|
the documented heuristic phrases (`isDeterministicVendor500` — `exceeds
|
|
389
|
-
maximum length`
|
|
411
|
+
maximum length`, `Missing Required Field`, `Value does not exist for the
|
|
412
|
+
required field`, and since issue #119 `Unable to find <field> in the <entity>
|
|
413
|
+
Entity.` — the live field-existence signature of an unknown `includeFields`
|
|
414
|
+
name) is PERMANENT: the transport makes exactly ONE
|
|
390
415
|
upstream call (it breaks out before the retry decision — the parsed body is
|
|
391
416
|
already in hand at the retry site), and the mapped error keeps the
|
|
392
417
|
`ServerError` class + `SERVER_ERROR` code + status 500 but carries
|
|
@@ -486,7 +511,7 @@ Host form: `https://<name>.autotask.net/atservicesrest` (lowercase
|
|
|
486
511
|
| `pageSize` | 10 | 500 (vendor max) | page size for query walks; `maxRecords` on the wire |
|
|
487
512
|
| `timeoutMs` | 30 000 | — | whole-call budget, incl. retries |
|
|
488
513
|
| `maxRetries` | 3 | — | the client's retry budget, overridable per call (`InvokeOptions.maxRetries` / `MutationOpts.maxRetries`; `0` = exactly one send per request the call performs). The budget caps three legs (issues #105/#109): the 429 leg (retried on GET/PUT/DELETE + flagged calls — unchanged) and the 5xx/network + non-JSON 2xx legs (GET + flagged only) |
|
|
489
|
-
| `maintenanceMaxWaitMs` | 3 600 000 (1 h) | — | **maintenance-window auto-retry** (weekly Autotask window): when the
|
|
514
|
+
| `maintenanceMaxWaitMs` | 3 600 000 (1 h) | — | **maintenance-window auto-retry** (weekly Autotask window): when the first 256 bytes of a non-JSON body read as a maintenance notice, the transport waits `maintenancePollMs` and retries for up to this long from the FIRST such page. A `2xx` page is waited out for reads AND writes (edge-served before execution); a `4xx`/`5xx` page only for calls safe to re-send (GET / explicitly idempotent-flagged) — an unflagged mutation makes ONE send and carries `details.maintenanceDetected` (a proxy can serve the page after forwarding). JSON bodies never enter. Does not consume `maxRetries`; each post-wait attempt gets a fresh `timeoutMs`. `0` disables the wait. A non-matching HTML-ish page stamps `details.maintenancePatternMiss`. Observed ceiling 4 h: `MAINTENANCE_WINDOW_MAX_MS` (19 windows: 30–240 min, median ≈ 2 h) |
|
|
490
515
|
| `maintenancePollMs` | 30 000 | — | poll interval while waiting out a maintenance page |
|
|
491
516
|
| `maxConcurrent` | 10 | 10 (vendor) | concurrency guard |
|
|
492
517
|
| `profile` | `library` | — | `library` = today's defaults (changes nothing); `agent` = the conservative `AGENT_PROFILE` limits for LLM-driven workloads (below). Resolution: explicit config > profile > library default |
|
|
@@ -675,9 +700,16 @@ carry paging parameters.
|
|
|
675
700
|
|
|
676
701
|
- **Page walk — cursor-follow on `pageDetails.nextPageUrl`** (node-style): page 1
|
|
677
702
|
POSTs the `QueryModel` body (`MaxRecords = pageSize`) to `/V1.0/<E>/query`;
|
|
678
|
-
later pages POST the
|
|
679
|
-
|
|
680
|
-
the
|
|
703
|
+
later pages POST the SAME body (`buildQueryBody` — `MaxRecords`,
|
|
704
|
+
`IncludeFields` with the `id` append, the preserved `filter`) to the ABSOLUTE
|
|
705
|
+
`nextPageUrl`, rebased to the client base URL (same-origin cursors verbatim;
|
|
706
|
+
cross-origin re-based — the node's custom-zone behaviour). Issue #118: before
|
|
707
|
+
the fix later pages POSTed `{ filter }` alone and returned the FULL vendor
|
|
708
|
+
model (live-reproduced 2026-10-19: page 1 of a three-field subset walk —
|
|
709
|
+
`id`, `title`, `status` — carried 4 keys, page 2 carried 76). The vendor HONOURS `IncludeFields` on a continuation and
|
|
710
|
+
IGNORES `MaxRecords` (it serves its own 500-row page; the walk slices to its
|
|
711
|
+
limit, so the RESULT is exact and the wire page size on page 2+ is the
|
|
712
|
+
vendor's).
|
|
681
713
|
- `Page<T>`: `{ items, page (1-based), pageSize, requestCount (the vendor
|
|
682
714
|
value verbatim — the MaxRecords echo, not a total), hasMore (nextPageUrl
|
|
683
715
|
!== null), totalPages }`. `totalPages` is the pages KNOWN so far —
|
|
@@ -926,6 +958,12 @@ the mode pinned (§7.4). The governor rules (pinned):
|
|
|
926
958
|
the SDK; the caller passes `limit` as the TOTAL cap (default 500, max
|
|
927
959
|
2500) and never owns a stream. `queryAll` keeps its raw behavior (the
|
|
928
960
|
unbounded walk; the catalog text steers the caller away for normal work);
|
|
961
|
+
- a query's `includeFields` NAMES are validated **pre-request** (issue
|
|
962
|
+
#119b): the live field metadata is the authority (the vendored spec when
|
|
963
|
+
that read faults), every unknown name is named in one typed
|
|
964
|
+
`CONFIG_ERROR` before the query POST, and `fieldCheck: false` in the query
|
|
965
|
+
bag disables the name check (the `string[]` / non-empty shape check always
|
|
966
|
+
runs);
|
|
929
967
|
- `dryRun` must be a real boolean: anything else (`'true'`, `0`, `null`) is
|
|
930
968
|
refused before any request — only an explicit `false` applies a write;
|
|
931
969
|
- `<resource>.createIfNotExists` takes its match fields as an optional
|
|
@@ -1426,6 +1464,16 @@ drift). Records exist only for plan rows that are implemented/tested; the
|
|
|
1426
1464
|
plan (`capabilities.plan.json`) is the single source of truth and the
|
|
1427
1465
|
registry is its compiled form.
|
|
1428
1466
|
|
|
1467
|
+
**Consumer-defaulted arguments (issue #115).** A manifest CONSUMER may fill an
|
|
1468
|
+
argument this registry marks required when the caller omits it — the manifest
|
|
1469
|
+
describes the SDK operation, not the consumer's tool contract. The one
|
|
1470
|
+
instance today is `contracts.profitability`: the SDK requires
|
|
1471
|
+
`dateFrom`/`dateTo`, and iit-mcp-autotask defaults both to the last full
|
|
1472
|
+
calendar month (UTC) when the caller omits them. The served
|
|
1473
|
+
`metadata.args`/`metadata.usage` and the record's
|
|
1474
|
+
`inputSchema.opts.description` state the caveat; a consumer that trusts the
|
|
1475
|
+
raw manifest should apply the same contract.
|
|
1476
|
+
|
|
1429
1477
|
### 9.1 Generated artifact #5: `src/relationships.ts` (the relationship map + zone facts)
|
|
1430
1478
|
|
|
1431
1479
|
The regen chain's 5th generated module (plan S12 §2.4: the 4 generated
|
|
@@ -1648,7 +1696,8 @@ retrying with the same body fails identically; fix the field instead
|
|
|
1648
1696
|
httpStatus >= 500). **Resolution (issue #23):** the *deterministic*
|
|
1649
1697
|
validation 500 is no longer labelled retryable — a 500 whose `errors[]` text
|
|
1650
1698
|
matches the documented heuristic phrases (`isDeterministicVendor500`:
|
|
1651
|
-
`exceeds maximum length`, `Missing Required Field`, `Value does not exist for the required field
|
|
1699
|
+
`exceeds maximum length`, `Missing Required Field`, `Value does not exist for the required field`,
|
|
1700
|
+
`Unable to find <field> in the <entity> Entity.` — issue #119, live-verified 2026-10-19) makes exactly ONE upstream call (the
|
|
1652
1701
|
transport skips the retry), keeps the `ServerError` class + `SERVER_ERROR`
|
|
1653
1702
|
code + status 500, but carries `retryable: false`, `details.deterministic:
|
|
1654
1703
|
true`, and a `suggestedAction` pointing at the field catalog
|
|
@@ -2027,7 +2076,11 @@ the TTL is the other expiry path.
|
|
|
2027
2076
|
|
|
2028
2077
|
**The on-demand op:** `operations.fieldCatalog(client, opts?)`
|
|
2029
2078
|
(`{ resources?, include?, fields?, labels?, fresh? }` — `fields` filters the
|
|
2030
|
-
fields-kind rows to the named fields and reports `unmatchedFields`;
|
|
2079
|
+
fields-kind rows to the named fields and reports `unmatchedFields`; a
|
|
2080
|
+
projected row is marked `projected: true` (issue #121 — the four derived
|
|
2081
|
+
lists then describe the PROJECTED set only, so a consumer can never read a
|
|
2082
|
+
projected `required`/`queryable` list as the resource's full picture; a full
|
|
2083
|
+
row omits the key); `labels`
|
|
2031
2084
|
adds the picklist labels; default: the five workflow
|
|
2032
2085
|
resources, `include: ['fields']`; the closed opts bag is documented in
|
|
2033
2086
|
the registry record). One bounded entityInformation GET per resource ×
|
|
@@ -2630,6 +2683,19 @@ query-verb-ONLY and zero-wire or vendor-column-selection-only:
|
|
|
2630
2683
|
billing item to its company / ticket / project / time entry / contract
|
|
2631
2684
|
service / contract / billing code / expense item, plus the date and the
|
|
2632
2685
|
amount fields — the "common narrow read" the issue's example targets.
|
|
2686
|
+
- **`fieldCheck: false` — the includeFields NAME check's opt-out (issue
|
|
2687
|
+
#119b).** By default the bounded `query` op validates every `includeFields`
|
|
2688
|
+
name BEFORE the query POST: the authority is the entity's LIVE field
|
|
2689
|
+
metadata (`getFieldInfo()` through the cache-aware channel of section
|
|
2690
|
+
11.14 — warm = zero wire), degrading to the vendored spec
|
|
2691
|
+
(`src/field-sets.ts`) when the metadata read faults; with neither an
|
|
2692
|
+
authority the check is skipped. The live set wins when present — a
|
|
2693
|
+
live-only field (`time_entries.postedDate`) is accepted and a
|
|
2694
|
+
vendored-only field the tenant no longer has is refused. Every unknown
|
|
2695
|
+
name is named in ONE typed `CONFIG_ERROR` (zero query wire), with
|
|
2696
|
+
`fieldCheck: false` as the escape hatch and `operations.fieldCatalog` as
|
|
2697
|
+
the discovery pointer. The shape check (`string[]`, non-empty entries)
|
|
2698
|
+
always runs.
|
|
2633
2699
|
- **The live check (the AC test for the presets).** Each preset's fields
|
|
2634
2700
|
are asserted to EXIST on the live resource against the LIVE field
|
|
2635
2701
|
catalogue (`operations.fieldCatalog` — the entityInformation/fields
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-autotask",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Fully-typed TypeScript SDK for the Autotask (Datto) PSA REST API. Zero runtime dependencies. Built for building MCP servers and integrations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"bin",
|
|
88
88
|
"dist",
|
|
89
89
|
"README.md",
|
|
90
|
+
"CHANGELOG.md",
|
|
90
91
|
"LICENSE",
|
|
91
92
|
"ARCHITECTURE.md",
|
|
92
93
|
"capabilities.json",
|
|
@@ -112,7 +113,7 @@
|
|
|
112
113
|
"coverage": "vitest run --coverage",
|
|
113
114
|
"lint": "eslint src test",
|
|
114
115
|
"lint:fix": "eslint src test --fix",
|
|
115
|
-
"verify": "npm run typecheck && npm run lint && npm run generate:check && npm run build && node scripts/check-capabilities.mjs --ship && npm run mcp:project:check && npm run n8n:project:check && npm run catalog:check && npm run mcp:check-surface && npm run public-surface && npm run verify:pack && npm test",
|
|
116
|
+
"verify": "npm run lockfile:check && npm run typecheck && npm run lint && npm run generate:check && npm run build && node scripts/check-capabilities.mjs --ship && npm run mcp:project:check && npm run n8n:project:check && npm run catalog:check && npm run mcp:check-surface && npm run public-surface && npm run verify:pack && npm test",
|
|
116
117
|
"plan:derive": "node scripts/derive-plan.mjs",
|
|
117
118
|
"generate:check": "node scripts/generate-batch.mjs --check && node scripts/generate-batch.mjs --check --group B",
|
|
118
119
|
"capabilities:build": "node scripts/generate-capabilities.mjs",
|
|
@@ -131,6 +132,7 @@
|
|
|
131
132
|
"n8n:project:check": "node scripts/project-n8n-mcp.mjs --check",
|
|
132
133
|
"verify:pack": "node scripts/verify-pack.mjs",
|
|
133
134
|
"release:check": "node scripts/check-release-plan-hash.mjs",
|
|
135
|
+
"lockfile:check": "node scripts/check-lockfile-sync.mjs",
|
|
134
136
|
"capabilities:check:ship": "node scripts/check-capabilities.mjs --ship",
|
|
135
137
|
"prepack": "npm run build",
|
|
136
138
|
"prepublishOnly": "npm run release:check && npm run clean && npm run verify && npm run capabilities:check:ship",
|