node-autotask 0.16.0 → 0.18.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 +10 -4
- package/MCP_TOOL_CATALOG.json +2 -2
- package/MCP_TOOL_MANIFEST.md +4 -4
- package/N8N_MCP_MANIFEST.json +2 -2
- package/N8N_MCP_MANIFEST.md +3 -3
- package/README.md +36 -0
- package/capabilities.json +1571 -1571
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-EWMG3HHU.cjs → chunk-5O5EPYBO.cjs} +1570 -1570
- package/dist/{chunk-L3TK4CN5.cjs → chunk-7I3F3AJJ.cjs} +429 -419
- package/dist/{chunk-VCGC6YCI.cjs → chunk-G564CHXM.cjs} +135 -135
- package/dist/{chunk-NB254T3F.js → chunk-HXOWEYV3.js} +49 -39
- package/dist/{chunk-JIDR3NPV.js → chunk-VV2UUW2J.js} +1570 -1570
- package/dist/{chunk-U6JUHHMB.js → chunk-VYUZZIEE.js} +66 -32
- package/dist/{chunk-GWALLMAM.js → chunk-W2WSZOZA.js} +2 -2
- package/dist/{chunk-MAYYTYV5.cjs → chunk-WRWX6WIK.cjs} +68 -34
- package/dist/{index-CUsvZndu.d.ts → index-BqPY04ZG.d.ts} +89 -16
- package/dist/{index-leMyBJze.d.cts → index-D7x3MkLK.d.cts} +89 -16
- package/dist/index.cjs +24 -22
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -4
- package/dist/operations/index.cjs +178 -105
- package/dist/operations/index.d.cts +22 -7
- package/dist/operations/index.d.ts +22 -7
- 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 +78 -15
- package/examples/basic.ts +2 -2
- package/examples/mcp-server-http.ts +5 -4
- package/examples/mcp-server.ts +3 -3
- package/package.json +3 -2
package/docs/API.md
CHANGED
|
@@ -81,15 +81,15 @@ flow.
|
|
|
81
81
|
Native global `fetch` (Node >= 24) — zero runtime dependencies across the
|
|
82
82
|
whole package. `HttpClient.request<T>(opts): Promise<T>` is the single
|
|
83
83
|
transport primitive (`RequestOptions { method, path, query?, body?, headers? }`):
|
|
84
|
-
JSON bodies, retries with backoff (budget `maxRetries`) in
|
|
85
|
-
#105): a 429 (a pre-processing refusal — the vendor rejected
|
|
86
|
-
executing it) is retried per `Retry-After` on the pre-#105 retriable set
|
|
84
|
+
JSON bodies, retries with backoff (budget `maxRetries`) in three legs
|
|
85
|
+
(issues #105/#109): a 429 (a pre-processing refusal — the vendor rejected
|
|
86
|
+
the call before executing it) is retried per `Retry-After` on the pre-#105 retriable set
|
|
87
87
|
(GET/PUT/DELETE + idempotent-flagged calls — an unflagged POST/PATCH surfaces
|
|
88
88
|
a 429 at once, as before #105), while 5xx/network errors are auto-retried only
|
|
89
89
|
for GET and idempotent-flagged calls: a mutation makes exactly one send on a
|
|
90
90
|
5xx or network error (the write-outcome is unknown once the body was written);
|
|
91
91
|
an explicit `idempotent: true` on a mutation opts back into the pre-#105
|
|
92
|
-
auto-retry on
|
|
92
|
+
auto-retry on all legs — plus a concurrency guard (`maxConcurrent`, default 10 — the API
|
|
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.
|
|
@@ -107,7 +107,22 @@ always, personal/business-sensitive data by default.
|
|
|
107
107
|
`code` (stable SCREAMING_SNAKE machine code), `category`, `retryable`,
|
|
108
108
|
`operation`, `httpStatus`, `vendorError` (raw vendor body under a documented
|
|
109
109
|
key), `resourceIds`, `suggestedAction` (deterministic next step only),
|
|
110
|
-
`correlationId`, `url`.
|
|
110
|
+
`correlationId`, `url`.
|
|
111
|
+
**`resourceIds` is the LEGACY, unlabeled id list (issue #116).** The stamp
|
|
112
|
+
sites name records of SEVERAL vendor entities — the failed record's own id
|
|
113
|
+
on a `get` miss (any entity), ticket / company / contact ids on the special
|
|
114
|
+
ops, a billing-code id on the `time_entries` allocation failures, staff ids
|
|
115
|
+
on the assignee/owner prechecks — and the field never says which. Every
|
|
116
|
+
SDK stamp site that sets `resourceIds` also sets `details.idRefs` — the
|
|
117
|
+
same ids, each labeled `{ entity, id }` with the vendor entity name
|
|
118
|
+
(singular
|
|
119
|
+
snake_case — `company`, `ticket`, `contact`, `billing_code`, `resource`
|
|
120
|
+
(staff), the same word the error message uses; the pairing is a guarantee
|
|
121
|
+
of the SDK stamp sites — a consumer-constructed error may set `resourceIds`
|
|
122
|
+
without `idRefs`). Machine consumers key on `idRefs`; `resourceIds` is kept
|
|
123
|
+
for compatibility, and neither is ever the input to `expandResourceNames`
|
|
124
|
+
(staff ids only — a colliding non-staff id resolves to an unrelated
|
|
125
|
+
person's name, section 7.7). The vendor documents no error bodies; the shape
|
|
111
126
|
`{ errors: [{ message, field?, code? }] }` is pinned from the node's
|
|
112
127
|
errorHandler and parsed defensively (`parseVendorErrors`,
|
|
113
128
|
`formatVendorDetail`). `errorFromStatus(status, body, url?, operation?,
|
|
@@ -151,8 +166,10 @@ served payload verbatim).
|
|
|
151
166
|
|
|
152
167
|
`buildFilter(conditions)` wraps caller conditions (multiple top-level
|
|
153
168
|
conditions are `and`-wrapped); `validateFilter` enforces the vendor caps
|
|
154
|
-
(section 5 below). `
|
|
155
|
-
(`{ 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
|
|
156
173
|
shared filter-shape gate of the query family (every query-family entry
|
|
157
174
|
point routes through it pre-request): the `filter` param accepts a
|
|
158
175
|
conditions array or `{ filter: [...] }`, and `undefined`/`null` are the
|
|
@@ -245,7 +262,7 @@ status → what the vendor does that triggers it.
|
|
|
245
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 maintenance-notice page is waited out up to `maintenanceMaxWaitMs` (default 1 h) for reads AND writes; on exhaustion `details.maintenance` reports the wait (section 2.1) |
|
|
246
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) |
|
|
247
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) |
|
|
248
|
-
| `ResolutionError` | `RESOLUTION_AMBIGUOUS` / `RESOLUTION_TRUNCATED` / `NOT_FOUND` | resolution | — | `resolve`: multiple exact matches (candidates in `resourceIds`); a cap-tripped bounded client scan (`resolution.scanOnMiss` only; never a silent `null`); a numeric-id miss |
|
|
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 |
|
|
249
266
|
| `PaginationLimitError` | `PAGINATION_LIMIT` | policy | — | a page walk that would exceed the 1000-page guard (`suggestedAction` names the tighter filter) |
|
|
250
267
|
| `QueryTooLongError` | `QUERY_TOO_LONG` | policy | — | caller-requested `via: 'get'` whose serialized URL would exceed the 2048-char GET cap — the SDK never silently degrades to POST |
|
|
251
268
|
| `StaleObjectError` | `STALE_OBJECT` | conflict | — | the caller-supplied `lastModifiedDate` no longer matches the vendor's current value (no `If-Match`/ETag exists) |
|
|
@@ -371,7 +388,10 @@ write that answered nothing is now a typed failure (strict default: no
|
|
|
371
388
|
|
|
372
389
|
**Deterministic validation 500 (#23).** A 500 whose `errors[]` entries match
|
|
373
390
|
the documented heuristic phrases (`isDeterministicVendor500` — `exceeds
|
|
374
|
-
maximum length`
|
|
391
|
+
maximum length`, `Missing Required Field`, `Value does not exist for the
|
|
392
|
+
required field`, and since issue #119 `Unable to find <field> in the <entity>
|
|
393
|
+
Entity.` — the live field-existence signature of an unknown `includeFields`
|
|
394
|
+
name) is PERMANENT: the transport makes exactly ONE
|
|
375
395
|
upstream call (it breaks out before the retry decision — the parsed body is
|
|
376
396
|
already in hand at the retry site), and the mapped error keeps the
|
|
377
397
|
`ServerError` class + `SERVER_ERROR` code + status 500 but carries
|
|
@@ -470,7 +490,7 @@ Host form: `https://<name>.autotask.net/atservicesrest` (lowercase
|
|
|
470
490
|
|---------|---------|-----|-------|
|
|
471
491
|
| `pageSize` | 10 | 500 (vendor max) | page size for query walks; `maxRecords` on the wire |
|
|
472
492
|
| `timeoutMs` | 30 000 | — | whole-call budget, incl. retries |
|
|
473
|
-
| `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
|
|
493
|
+
| `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) |
|
|
474
494
|
| `maintenanceMaxWaitMs` | 3 600 000 (1 h) | — | **maintenance-window auto-retry** (weekly Autotask window): when the edge answers a non-JSON HTML page that reads as a maintenance notice, the transport waits `maintenancePollMs` and retries for up to this long from the FIRST such page — for reads AND writes (the edge answered before the vendor application saw the request; the weekly window is typically under 30 min). Does not consume `maxRetries`; each post-wait attempt gets a fresh `timeoutMs`. `0` disables the wait. A page that does not look like maintenance (e.g. a wrong `baseUrl`) never enters the loop |
|
|
475
495
|
| `maintenancePollMs` | 30 000 | — | poll interval while waiting out a maintenance page |
|
|
476
496
|
| `maxConcurrent` | 10 | 10 (vendor) | concurrency guard |
|
|
@@ -660,9 +680,16 @@ carry paging parameters.
|
|
|
660
680
|
|
|
661
681
|
- **Page walk — cursor-follow on `pageDetails.nextPageUrl`** (node-style): page 1
|
|
662
682
|
POSTs the `QueryModel` body (`MaxRecords = pageSize`) to `/V1.0/<E>/query`;
|
|
663
|
-
later pages POST the
|
|
664
|
-
|
|
665
|
-
the
|
|
683
|
+
later pages POST the SAME body (`buildQueryBody` — `MaxRecords`,
|
|
684
|
+
`IncludeFields` with the `id` append, the preserved `filter`) to the ABSOLUTE
|
|
685
|
+
`nextPageUrl`, rebased to the client base URL (same-origin cursors verbatim;
|
|
686
|
+
cross-origin re-based — the node's custom-zone behaviour). Issue #118: before
|
|
687
|
+
the fix later pages POSTed `{ filter }` alone and returned the FULL vendor
|
|
688
|
+
model (live-reproduced 2026-10-19: page 1 of a three-field subset walk —
|
|
689
|
+
`id`, `title`, `status` — carried 4 keys, page 2 carried 76). The vendor HONOURS `IncludeFields` on a continuation and
|
|
690
|
+
IGNORES `MaxRecords` (it serves its own 500-row page; the walk slices to its
|
|
691
|
+
limit, so the RESULT is exact and the wire page size on page 2+ is the
|
|
692
|
+
vendor's).
|
|
666
693
|
- `Page<T>`: `{ items, page (1-based), pageSize, requestCount (the vendor
|
|
667
694
|
value verbatim — the MaxRecords echo, not a total), hasMore (nextPageUrl
|
|
668
695
|
!== null), totalPages }`. `totalPages` is the pages KNOWN so far —
|
|
@@ -911,6 +938,12 @@ the mode pinned (§7.4). The governor rules (pinned):
|
|
|
911
938
|
the SDK; the caller passes `limit` as the TOTAL cap (default 500, max
|
|
912
939
|
2500) and never owns a stream. `queryAll` keeps its raw behavior (the
|
|
913
940
|
unbounded walk; the catalog text steers the caller away for normal work);
|
|
941
|
+
- a query's `includeFields` NAMES are validated **pre-request** (issue
|
|
942
|
+
#119b): the live field metadata is the authority (the vendored spec when
|
|
943
|
+
that read faults), every unknown name is named in one typed
|
|
944
|
+
`CONFIG_ERROR` before the query POST, and `fieldCheck: false` in the query
|
|
945
|
+
bag disables the name check (the `string[]` / non-empty shape check always
|
|
946
|
+
runs);
|
|
914
947
|
- `dryRun` must be a real boolean: anything else (`'true'`, `0`, `null`) is
|
|
915
948
|
refused before any request — only an explicit `false` applies a write;
|
|
916
949
|
- `<resource>.createIfNotExists` takes its match fields as an optional
|
|
@@ -1097,6 +1130,18 @@ can be blind to the API user while per-id reads work, so an `id in`
|
|
|
1097
1130
|
lookup on a blind table would silently return nothing; the per-id GET is
|
|
1098
1131
|
the authority read (section 11.2).
|
|
1099
1132
|
|
|
1133
|
+
**The input is Resource (staff) ids only (issue #116).** Because the op's
|
|
1134
|
+
one wire verb is `GET /V1.0/Resources/{id}`, an id of any other vendor
|
|
1135
|
+
entity (a ticket / company / contact / billing-code id) resolves to an
|
|
1136
|
+
unrelated person's name when it collides with a live staff id (and the
|
|
1137
|
+
wrong name then sticks in the default-on `resourceNameCache` under that id
|
|
1138
|
+
until TTL — a one-off mis-feed keeps serving it; section 11.21), or to a
|
|
1139
|
+
misleading `blind: true` marker when it does not. An `AutotaskError`'s
|
|
1140
|
+
legacy `resourceIds` may carry exactly those (the field is unlabeled —
|
|
1141
|
+
section 1.5): a consumer must filter the error's `details.idRefs`
|
|
1142
|
+
(`{ entity, id }`) on `entity: 'resource'` before feeding ids here — never
|
|
1143
|
+
the unlabeled field.
|
|
1144
|
+
|
|
1100
1145
|
- **Input**: `ids: (number | string)[]` — vendor numeric resource ids
|
|
1101
1146
|
(safe integers, or the canonical integer string for ids above 2^53 —
|
|
1102
1147
|
the exact digits ride the wire URL verbatim). Non-numeric string ids
|
|
@@ -1621,7 +1666,8 @@ retrying with the same body fails identically; fix the field instead
|
|
|
1621
1666
|
httpStatus >= 500). **Resolution (issue #23):** the *deterministic*
|
|
1622
1667
|
validation 500 is no longer labelled retryable — a 500 whose `errors[]` text
|
|
1623
1668
|
matches the documented heuristic phrases (`isDeterministicVendor500`:
|
|
1624
|
-
`exceeds maximum length`, `Missing Required Field`, `Value does not exist for the required field
|
|
1669
|
+
`exceeds maximum length`, `Missing Required Field`, `Value does not exist for the required field`,
|
|
1670
|
+
`Unable to find <field> in the <entity> Entity.` — issue #119, live-verified 2026-10-19) makes exactly ONE upstream call (the
|
|
1625
1671
|
transport skips the retry), keeps the `ServerError` class + `SERVER_ERROR`
|
|
1626
1672
|
code + status 500, but carries `retryable: false`, `details.deterministic:
|
|
1627
1673
|
true`, and a `suggestedAction` pointing at the field catalog
|
|
@@ -2000,7 +2046,11 @@ the TTL is the other expiry path.
|
|
|
2000
2046
|
|
|
2001
2047
|
**The on-demand op:** `operations.fieldCatalog(client, opts?)`
|
|
2002
2048
|
(`{ resources?, include?, fields?, labels?, fresh? }` — `fields` filters the
|
|
2003
|
-
fields-kind rows to the named fields and reports `unmatchedFields`;
|
|
2049
|
+
fields-kind rows to the named fields and reports `unmatchedFields`; a
|
|
2050
|
+
projected row is marked `projected: true` (issue #121 — the four derived
|
|
2051
|
+
lists then describe the PROJECTED set only, so a consumer can never read a
|
|
2052
|
+
projected `required`/`queryable` list as the resource's full picture; a full
|
|
2053
|
+
row omits the key); `labels`
|
|
2004
2054
|
adds the picklist labels; default: the five workflow
|
|
2005
2055
|
resources, `include: ['fields']`; the closed opts bag is documented in
|
|
2006
2056
|
the registry record). One bounded entityInformation GET per resource ×
|
|
@@ -2603,6 +2653,19 @@ query-verb-ONLY and zero-wire or vendor-column-selection-only:
|
|
|
2603
2653
|
billing item to its company / ticket / project / time entry / contract
|
|
2604
2654
|
service / contract / billing code / expense item, plus the date and the
|
|
2605
2655
|
amount fields — the "common narrow read" the issue's example targets.
|
|
2656
|
+
- **`fieldCheck: false` — the includeFields NAME check's opt-out (issue
|
|
2657
|
+
#119b).** By default the bounded `query` op validates every `includeFields`
|
|
2658
|
+
name BEFORE the query POST: the authority is the entity's LIVE field
|
|
2659
|
+
metadata (`getFieldInfo()` through the cache-aware channel of section
|
|
2660
|
+
11.14 — warm = zero wire), degrading to the vendored spec
|
|
2661
|
+
(`src/field-sets.ts`) when the metadata read faults; with neither an
|
|
2662
|
+
authority the check is skipped. The live set wins when present — a
|
|
2663
|
+
live-only field (`time_entries.postedDate`) is accepted and a
|
|
2664
|
+
vendored-only field the tenant no longer has is refused. Every unknown
|
|
2665
|
+
name is named in ONE typed `CONFIG_ERROR` (zero query wire), with
|
|
2666
|
+
`fieldCheck: false` as the escape hatch and `operations.fieldCatalog` as
|
|
2667
|
+
the discovery pointer. The shape check (`string[]`, non-empty entries)
|
|
2668
|
+
always runs.
|
|
2606
2669
|
- **The live check (the AC test for the presets).** Each preset's fields
|
|
2607
2670
|
are asserted to EXIST on the live resource against the LIVE field
|
|
2608
2671
|
catalogue (`operations.fieldCatalog` — the entityInformation/fields
|
package/examples/basic.ts
CHANGED
|
@@ -32,8 +32,8 @@ import { AutotaskClient, type ZoneName } from 'node-autotask';
|
|
|
32
32
|
const apiCode = process.env.AUTOTASK_TRACKING_IDENTIFIER ?? process.env.AUTOTASK_API_CODE ?? process.env.AUTOTASK_API_INTEGRATION_CODE;
|
|
33
33
|
const user = process.env.AUTOTASK_USERNAME ?? process.env.AUTOTASK_USER;
|
|
34
34
|
const secret = process.env.AUTOTASK_SECRET;
|
|
35
|
-
const zone = process.env.AUTOTASK_ZONE;
|
|
36
|
-
const impersonation = process.env.AUTOTASK_IMPERSONATION_ID;
|
|
35
|
+
const zone = process.env.AUTOTASK_ZONE?.trim() || undefined;
|
|
36
|
+
const impersonation = process.env.AUTOTASK_IMPERSONATION_ID?.trim() || undefined;
|
|
37
37
|
|
|
38
38
|
const DRY = !(apiCode && user && secret);
|
|
39
39
|
|
|
@@ -95,13 +95,14 @@ import {
|
|
|
95
95
|
GATEWAY_WARMUP_BUDGET_MS,
|
|
96
96
|
} from './mcp-gateway.js';
|
|
97
97
|
|
|
98
|
-
const
|
|
98
|
+
const PORT_RAW = process.env.PORT?.trim();
|
|
99
|
+
const PORT = PORT_RAW !== undefined && PORT_RAW !== '' ? Number(PORT_RAW) : 7437;
|
|
99
100
|
const apiCode = process.env.AUTOTASK_TRACKING_IDENTIFIER ?? process.env.AUTOTASK_API_CODE ?? process.env.AUTOTASK_API_INTEGRATION_CODE;
|
|
100
101
|
const user = process.env.AUTOTASK_USERNAME ?? process.env.AUTOTASK_USER;
|
|
101
102
|
const secret = process.env.AUTOTASK_SECRET;
|
|
102
|
-
const zone = process.env.AUTOTASK_ZONE;
|
|
103
|
-
const impersonation = process.env.AUTOTASK_IMPERSONATION_ID;
|
|
104
|
-
const baseUrl = process.env.AUTOTASK_BASE_URL;
|
|
103
|
+
const zone = process.env.AUTOTASK_ZONE?.trim() || undefined;
|
|
104
|
+
const impersonation = process.env.AUTOTASK_IMPERSONATION_ID?.trim() || undefined;
|
|
105
|
+
const baseUrl = process.env.AUTOTASK_BASE_URL?.trim() || undefined;
|
|
105
106
|
/** The explicit env-vs-gateway switch (issue #5): "on" = credentials per request, headers only. */
|
|
106
107
|
const GATEWAY = (process.env.AUTOTASK_GATEWAY_MODE ?? 'off').toLowerCase() === 'on';
|
|
107
108
|
/** Env mode: DRY when no credentials. Gateway mode is never "DRY" — a request
|
package/examples/mcp-server.ts
CHANGED
|
@@ -64,9 +64,9 @@ import { withEnvOverrides } from './mcp-env-config.js';
|
|
|
64
64
|
const apiCode = process.env.AUTOTASK_TRACKING_IDENTIFIER ?? process.env.AUTOTASK_API_CODE ?? process.env.AUTOTASK_API_INTEGRATION_CODE;
|
|
65
65
|
const user = process.env.AUTOTASK_USERNAME ?? process.env.AUTOTASK_USER;
|
|
66
66
|
const secret = process.env.AUTOTASK_SECRET;
|
|
67
|
-
const zone = process.env.AUTOTASK_ZONE;
|
|
68
|
-
const impersonation = process.env.AUTOTASK_IMPERSONATION_ID;
|
|
69
|
-
const baseUrl = process.env.AUTOTASK_BASE_URL;
|
|
67
|
+
const zone = process.env.AUTOTASK_ZONE?.trim() || undefined;
|
|
68
|
+
const impersonation = process.env.AUTOTASK_IMPERSONATION_ID?.trim() || undefined;
|
|
69
|
+
const baseUrl = process.env.AUTOTASK_BASE_URL?.trim() || undefined;
|
|
70
70
|
const DRY = !(apiCode && user && secret);
|
|
71
71
|
|
|
72
72
|
/** One client for the whole process (fail-closed construction; null in DRY mode). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-autotask",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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,
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"coverage": "vitest run --coverage",
|
|
113
113
|
"lint": "eslint src test",
|
|
114
114
|
"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",
|
|
115
|
+
"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
116
|
"plan:derive": "node scripts/derive-plan.mjs",
|
|
117
117
|
"generate:check": "node scripts/generate-batch.mjs --check && node scripts/generate-batch.mjs --check --group B",
|
|
118
118
|
"capabilities:build": "node scripts/generate-capabilities.mjs",
|
|
@@ -131,6 +131,7 @@
|
|
|
131
131
|
"n8n:project:check": "node scripts/project-n8n-mcp.mjs --check",
|
|
132
132
|
"verify:pack": "node scripts/verify-pack.mjs",
|
|
133
133
|
"release:check": "node scripts/check-release-plan-hash.mjs",
|
|
134
|
+
"lockfile:check": "node scripts/check-lockfile-sync.mjs",
|
|
134
135
|
"capabilities:check:ship": "node scripts/check-capabilities.mjs --ship",
|
|
135
136
|
"prepack": "npm run build",
|
|
136
137
|
"prepublishOnly": "npm run release:check && npm run clean && npm run verify && npm run capabilities:check:ship",
|