node-autotask 0.33.0 → 0.34.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/CHANGELOG.md +32 -0
- 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/capabilities.json +4084 -2544
- package/capabilities.schema.json +32 -0
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.d.cts +13 -1
- package/dist/capabilities.d.ts +13 -1
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-VKE56TTN.js → chunk-4MUBUSTL.js} +4083 -2543
- package/dist/{chunk-5DS6F6GF.js → chunk-6ADFCU3I.js} +399 -342
- package/dist/{chunk-OO67MRHN.cjs → chunk-DKJP73FX.cjs} +966 -909
- package/dist/{chunk-MFDQHFGJ.js → chunk-EZKM4HQ6.js} +1017 -577
- package/dist/{chunk-SP2SJJZX.cjs → chunk-FOOHJBPP.cjs} +4083 -2543
- package/dist/{chunk-4QN43TRP.cjs → chunk-GDQ4QM7Q.cjs} +1066 -626
- package/dist/{chunk-3LAZZT7V.cjs → chunk-P6YN2POI.cjs} +143 -143
- package/dist/{chunk-7WNE5MPG.js → chunk-RDFL5QYE.js} +2 -2
- package/dist/{index-BaeMHZPk.d.ts → index-ByMU8IC2.d.ts} +498 -198
- package/dist/{index-Cq0VsfSz.d.cts → index-Dx4-zpDo.d.cts} +498 -198
- package/dist/index.cjs +23 -23
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/operations/index.cjs +157 -116
- package/dist/operations/index.d.cts +39 -25
- package/dist/operations/index.d.ts +39 -25
- package/dist/operations/index.js +47 -6
- package/dist/pool.cjs +9 -9
- 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 +160 -35
- package/package.json +1 -1
package/dist/resources/index.js
CHANGED
|
@@ -136,9 +136,9 @@ import {
|
|
|
136
136
|
VersionInformationResource,
|
|
137
137
|
VersionResource,
|
|
138
138
|
ZoneInformationResource
|
|
139
|
-
} from "../chunk-
|
|
140
|
-
import "../chunk-
|
|
141
|
-
import "../chunk-
|
|
139
|
+
} from "../chunk-6ADFCU3I.js";
|
|
140
|
+
import "../chunk-EZKM4HQ6.js";
|
|
141
|
+
import "../chunk-4MUBUSTL.js";
|
|
142
142
|
export {
|
|
143
143
|
AppointmentsResource,
|
|
144
144
|
AuthenticateResource,
|
package/docs/API.md
CHANGED
|
@@ -953,6 +953,31 @@ out entirely. `cache.store` is otherwise a plain advanced seam: a supplied
|
|
|
953
953
|
`ReadCache` instance is used as-is, and `ttlMs` / `perResourceTtlMs` beside it
|
|
954
954
|
refuse (the instance owns its TTLs).
|
|
955
955
|
|
|
956
|
+
### Fresh single-record reads (`fresh: true`, issue #250)
|
|
957
|
+
|
|
958
|
+
A write made ELSEWHERE (another process, the Autotask UI, a client with a
|
|
959
|
+
private cache) does not void this client's read cache, so a read-back can be
|
|
960
|
+
served a snapshot. Every single-record `get` takes a trailing per-call
|
|
961
|
+
`{ fresh: true }` — `client.companies.get(id, { fresh: true })`, parent-scoped
|
|
962
|
+
`client.companyAlerts.get(parentId, id, { fresh: true })` — which skips the
|
|
963
|
+
cache LOOKUP, always issues the wire read and refreshes the entry with the
|
|
964
|
+
response (the default, no option or `fresh: false`, is the unchanged cached
|
|
965
|
+
read). Through the governed path pass `invokeRead(client, 'companies.get',
|
|
966
|
+
[id], { fresh: true })`: `InvokeOptions.fresh` is honoured ONLY on a
|
|
967
|
+
`<resource>.get` read; any other operation, a write, or a non-boolean value
|
|
968
|
+
refuses pre-request (zero wire). It affects the one read only: it never clears
|
|
969
|
+
other entries (unlike `http.readCache.clear()`).
|
|
970
|
+
|
|
971
|
+
Stale-response safety: the read cache does not coalesce concurrent reads (every
|
|
972
|
+
miss and every `fresh` call issues its own request), so a `fresh` read never
|
|
973
|
+
joins a read that started before a write. A read in flight across a write
|
|
974
|
+
through the same cache (including the pool's shared per-tenant cache, whose
|
|
975
|
+
epoch the write bumps) has its late store discarded, so a pre-write response is
|
|
976
|
+
never re-cached; the in-flight caller itself still receives the response it
|
|
977
|
+
asked for. Not covered: a write on a client whose cache is private (pool
|
|
978
|
+
`sharedReadCache: false`) is invisible to
|
|
979
|
+
the other client's cache — use `fresh` there.
|
|
980
|
+
|
|
956
981
|
## 7. Operations surface (`./operations` subpath)
|
|
957
982
|
|
|
958
983
|
Cross-resource helpers live in `src/operations/` (placement locked —
|
|
@@ -1484,7 +1509,14 @@ is closed when its label is complete, completed, closed, cancelled, canceled or
|
|
|
1484
1509
|
resolved. When the catalog cannot be read, or carries no recognisable closed
|
|
1485
1510
|
label, the historical id `5` stands in with `source: 'assumed'` and a `note`
|
|
1486
1511
|
that says so. Passing `excludeStatusIds` reports those ids instead
|
|
1487
|
-
(`source: 'picklist'` — an explicit caller choice).
|
|
1512
|
+
(`source: 'picklist'` — an explicit caller choice). `allowAssumed: false`
|
|
1513
|
+
(default `true`, behaviour unchanged) refuses the fallback: an unreadable
|
|
1514
|
+
catalog or one with no closed label throws an `AutotaskError` with code
|
|
1515
|
+
`STATUS_SEMANTICS_UNRESOLVED` instead of returning `[5]`; the same option is
|
|
1516
|
+
accepted by `tickets.forResource` and `companies.fullProfile`, and
|
|
1517
|
+
`companies.fullProfile` also takes `excludeStatusIds` (an operator pin reaches
|
|
1518
|
+
its `openTickets` part, with no catalog read). In `fullProfile` the throw comes
|
|
1519
|
+
at the `openTickets` read, after the company and the four other part reads. No module outside this
|
|
1488
1520
|
resolver holds a status literal: the move engine's closed-target refusal,
|
|
1489
1521
|
`companies.fullProfile`'s `openTickets` part and `tickets.forResource`'s
|
|
1490
1522
|
`openOnly` all call it and carry the disclosure (`closedStatusIds` /
|
|
@@ -1499,16 +1531,37 @@ default `both`; the seam is `Tickets.assignedResourceID` for primary and the
|
|
|
1499
1531
|
both `id in [...]`-chunked when the link set is large) unioned client-side —
|
|
1500
1532
|
deliberately not the cross-field grouped `or`, whose live semantics are
|
|
1501
1533
|
unverified. Result: `{ resourceID, scope, openOnly, tickets, primaryIds,
|
|
1502
|
-
secondaryIds, bothIds, counts, capped, unresolvedIds, closedStatus }`,
|
|
1503
|
-
|
|
1534
|
+
secondaryIds, bothIds, counts, capped, unresolvedIds, closedStatus }`, ascending
|
|
1535
|
+
by `id`, each ticket projected to `id`, `ticketNumber`, `title`,
|
|
1504
1536
|
`status`, `assignedResourceID`, `companyID`. `openOnly: true` applies the
|
|
1505
1537
|
resolved open filter server-side (`status notIn` the closed ids, or a single
|
|
1506
1538
|
`not-eq` leaf) and re-applies it client-side so a server that ignores the leaf
|
|
1507
1539
|
cannot widen the answer; `closedStatus` carries the disclosure above.
|
|
1508
1540
|
`limit` (1-500, default 100) caps the returned tickets — `capped: true` means
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1541
|
+
rows were actually left out (the primary read asks for `limit + 1` rows; exactly
|
|
1542
|
+
`limit` rows is not capped), and the link read is one 500-row page (`capped` is
|
|
1543
|
+
true when the vendor reports a further page; `countOnly` below pages further). `unresolvedIds` lists ids the
|
|
1544
|
+
ticket read did not return (deleted or invisible); under `openOnly`, tickets the
|
|
1545
|
+
open filter dropped because they are closed are NOT listed (one extra unfiltered
|
|
1546
|
+
id read per chunk tells them apart). `excludeStatusIds` overrides the closed set for this
|
|
1547
|
+
call.
|
|
1548
|
+
|
|
1549
|
+
`countOnly: true` (issue #248) returns counts only, no ticket rows:
|
|
1550
|
+
`{ resourceID, scope, primary, secondary, both, open, capped, closedStatus }`
|
|
1551
|
+
(typed overloads: `countOnly: true` narrows the return type to
|
|
1552
|
+
`TicketsForResourceCountResult`). Every figure counts OPEN tickets (`openOnly`
|
|
1553
|
+
and `limit` do not apply). `primary` is the `tickets.count` path with the filter
|
|
1554
|
+
`assignedResourceID eq R` and the open leaf, so it equals that call;
|
|
1555
|
+
`secondary` is the de-duplicated count of open secondary ticket ids (the link
|
|
1556
|
+
table has no status, so the open filter is an ids-only `id in` read per 100
|
|
1557
|
+
link ids); `both` is the overlap and `open` the union
|
|
1558
|
+
(`primary + secondary - both`). The link read follows the vendor's next-page
|
|
1559
|
+
links (ids only, 500 per page) up to 20 pages, so the count is exact below
|
|
1560
|
+
10,000 links; `capped: true` means the cap was hit with pages remaining, so
|
|
1561
|
+
`secondary`, `both` and `open` may undercount; it is never a silent undercount.
|
|
1562
|
+
The secondary read re-applies the open filter client-side, and `limit` is not
|
|
1563
|
+
validated (it does not apply). With `scope` `primary`/`secondary` only that side is
|
|
1564
|
+
read and `both` is 0.
|
|
1512
1565
|
|
|
1513
1566
|
## 8. Resource surface (135 resources)
|
|
1514
1567
|
|
|
@@ -2238,38 +2291,84 @@ budget or guessing:
|
|
|
2238
2291
|
|
|
2239
2292
|
### 11.12 Zone-fact create guidance (issue #12)
|
|
2240
2293
|
|
|
2241
|
-
**`operations.createGuidance`** (`{ resources? }`) serves the plan's zone
|
|
2242
|
-
facts per
|
|
2243
|
-
parent-scoping, the live-proven prefix, the `unproven` flag,
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2294
|
+
**`operations.createGuidance`** (`{ resources?, kind? }`) serves the plan's zone
|
|
2295
|
+
facts per create op — ZERO vendor calls: the `createOp` key, the
|
|
2296
|
+
parent-scoping, the live-proven prefix, the `unproven` flag, the blocker
|
|
2297
|
+
`{ kind, message, remedy, referenceField?, unsatisfiableViaApi?, requires? }`,
|
|
2298
|
+
the exact vendor text in `vendorEvidence`, and the spec-derived
|
|
2299
|
+
`deleteBoundary`.
|
|
2300
|
+
|
|
2301
|
+
**Row counts.** The backing plan table `zoneFacts.resources` has 99 entries;
|
|
2302
|
+
97 of them carry a `createGuidance` row — exactly one per plan create op, set
|
|
2303
|
+
equality guarded by clause 15 — and the other 2 (`billing_codes`,
|
|
2304
|
+
`resources`, the blind no-create tables) carry zone quirks only. Earlier
|
|
2305
|
+
revisions of this section quoted 92 or 94; the counts are 99 entries / 97 rows.
|
|
2306
|
+
`counts.total` is 97 and `counts.byKind` sums to it.
|
|
2307
|
+
|
|
2308
|
+
**Model-facing by construction (issue #246).** `blocker.message` and
|
|
2309
|
+
`blocker.remedy` are written for the consumer of the guidance: they carry no
|
|
2310
|
+
campaign markers, branch names, W-codes, dates, tenant record ids or
|
|
2311
|
+
maintainer instructions. Anything of that kind that is worth keeping lives in
|
|
2312
|
+
`vendorEvidence` (entries prefixed `maintainer provenance`), next to the
|
|
2313
|
+
verbatim vendor text. Clause 15 fails the build when a message or remedy
|
|
2314
|
+
matches a provenance pattern. A consumer that renders a blocker can therefore
|
|
2315
|
+
serve `message`/`remedy` as they are.
|
|
2316
|
+
|
|
2317
|
+
**Blocker kinds.** The closed set is `ok`, `blind-reference`, `blind-parent`,
|
|
2318
|
+
`picklist-unexposed`, `undocumented-ladder`, `resource-role-pair`,
|
|
2319
|
+
`delete-absent`, `non-mutable` and `unknown`.
|
|
2320
|
+
|
|
2321
|
+
- `delete-absent` is **not** a create blocker. A resource with no vendor
|
|
2322
|
+
DELETE created fine; that is the delete boundary (`deleteBoundary.available:
|
|
2323
|
+
false`, plus the `metadata.deleteBoundary` stamp on the create op). The
|
|
2324
|
+
eight rows that carried the kind (`tickets`, `companies`, `phases`,
|
|
2325
|
+
`company_notes`, ...) are now kind `ok` with the boundary on
|
|
2326
|
+
`deleteBoundary`; the kind is reserved (still accepted by the `kind` filter,
|
|
2327
|
+
0 rows) and clause 15 fails a row that carries it.
|
|
2328
|
+
- `non-mutable` **is** a create blocker: the vendor refuses the create write
|
|
2329
|
+
itself for the entity. No row carries it today.
|
|
2330
|
+
- `blind-reference` rows always name `referenceField` (the field whose
|
|
2331
|
+
referenced table is blind for this API user), including the contract
|
|
2332
|
+
billing-rule / exclusion / service / bundle rows.
|
|
2333
|
+
- `blocker.unsatisfiableViaApi: true` is the structured flag for creates the
|
|
2334
|
+
API cannot satisfy for any input: exactly the `picklist-unexposed` rows (the
|
|
2335
|
+
attachment `Type` picklist) and the `undocumented-ladder` row
|
|
2336
|
+
(`opportunities`). Clause 15 holds the flag and the kind in step.
|
|
2337
|
+
|
|
2338
|
+
**Where to read the company (issue #246).** Some resources have no `companyID`
|
|
2339
|
+
of their own. `row.parentCompany?: { via, resource, field }` says which parent
|
|
2340
|
+
record carries it: `tasks` serves `{ via: 'projectID', resource: 'projects',
|
|
2341
|
+
field: 'companyID' }` (read the project, then its `companyID`). The hint is
|
|
2342
|
+
derived from the vendored spec (a resource with no `companyID` whose scoping
|
|
2343
|
+
parent field points at a parent that has one) and clause 15 requires the plan
|
|
2344
|
+
row to equal that derivation, so it cannot drift. A resource with its own
|
|
2345
|
+
`companyID` carries no hint.
|
|
2256
2346
|
|
|
2257
2347
|
**Which required set wins (issue #219 N4).** Three sources answer "what does
|
|
2258
2348
|
this create really require", at different coverage: the live-proven prefix
|
|
2259
|
-
(
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
`
|
|
2265
|
-
|
|
2266
|
-
`
|
|
2267
|
-
`
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
`
|
|
2271
|
-
`
|
|
2272
|
-
|
|
2349
|
+
(the fields a live create actually demanded, in vendor-demand order), the
|
|
2350
|
+
hand-authored zone set `createRequiredSet`, and the vendor field catalog's
|
|
2351
|
+
`isRequired` (the spec opinion, wrong for most resources that carry a proven
|
|
2352
|
+
prefix). The precedence is FIXED: **live-proven > zone > spec**, and the source
|
|
2353
|
+
is named on the result. `effectiveRequiredFields(record, zoneFacts)` in
|
|
2354
|
+
`node-autotask/operations` is the only place in the SDK that compares the
|
|
2355
|
+
three; every `operations.createGuidance` row carries its result as
|
|
2356
|
+
`requiredFields: { fields, from, unproven, conditional? }`
|
|
2357
|
+
(`contacts.create` returns the four live-proven fields, not the empty spec list).
|
|
2358
|
+
|
|
2359
|
+
- `fields` is the **unconditional** set. A field the row gates on a picklist
|
|
2360
|
+
value (`requiresByValue`) is removed from it: `contracts.create` returns
|
|
2361
|
+
`status, contractType, contractName, startDate, endDate,
|
|
2362
|
+
timeReportingRequiresStartAndStopTimes, companyID`, and
|
|
2363
|
+
`estimatedRevenue` / `estimatedHours` / `estimatedCost` appear only in
|
|
2364
|
+
`conditional` (contractType 1 needs all three, 3/4/6/8 need
|
|
2365
|
+
`estimatedRevenue`). **Changed in 0.34.0:** the type-1 triple was previously
|
|
2366
|
+
returned inside `fields`.
|
|
2367
|
+
- `conditional?` is the row's `requiresByValue`, copied as typed gates; it is
|
|
2368
|
+
absent when the row declares none.
|
|
2369
|
+
- `unproven` is `true` when no live create is known to succeed with the set:
|
|
2370
|
+
the plan row is unproven (the create still failed after the prefix), or the
|
|
2371
|
+
set is only the vendor spec's opinion. It is additive on the result.
|
|
2273
2372
|
|
|
2274
2373
|
**Structured create requirements (issue #219 N3).** The blocker channel is a
|
|
2275
2374
|
CLASS of wall, not a statement of what the vendor wants; and the campaign
|
|
@@ -2305,6 +2404,32 @@ clause that stops this fact from being lost a second time. The create op's
|
|
|
2305
2404
|
registry record carries the same typed block as `metadata.createRequirements`
|
|
2306
2405
|
(the `autotask_describe` channel).
|
|
2307
2406
|
|
|
2407
|
+
### 11.12a Per-operation caveats (issue #249)
|
|
2408
|
+
|
|
2409
|
+
An operation record may carry `metadata.caveats`, an optional list of
|
|
2410
|
+
structured facts about the operation itself, served wherever operation
|
|
2411
|
+
metadata is (the `autotask_describe` channel, `capabilities.json`,
|
|
2412
|
+
`node-autotask/capabilities`). One row shape over a CLOSED kind set:
|
|
2413
|
+
|
|
2414
|
+
```ts
|
|
2415
|
+
{ kind: 'not-persisted'; fields?: string[]; note: string; remedyOp?: string }
|
|
2416
|
+
```
|
|
2417
|
+
|
|
2418
|
+
- `not-persisted`: the op accepts the listed `fields` but the vendor does not
|
|
2419
|
+
store them. `fields` is required for this kind; `remedyOp` names the plan
|
|
2420
|
+
operation that does what this one cannot.
|
|
2421
|
+
- First rows: `time_entries.update` and `ticket_charges.update` declare
|
|
2422
|
+
`{ kind: 'not-persisted', fields: ['ticketID'], remedyOp:
|
|
2423
|
+
'<resource>.moveToTicket' }`.
|
|
2424
|
+
- The kind set is closed at build time: `scripts/generate-capabilities.mjs`
|
|
2425
|
+
throws on an unknown kind or a malformed row, and gate clause 22 also checks
|
|
2426
|
+
that every named field exists on the resource model and every `remedyOp` is
|
|
2427
|
+
a plan operation.
|
|
2428
|
+
- Purely additive: `metadata.usage`, `metadata.pointerNote` and every other
|
|
2429
|
+
served field are unchanged, and a record without caveats has no `caveats`
|
|
2430
|
+
key. Unlike `pointerNote`, caveats ride the describe channel only (they are
|
|
2431
|
+
not attached to error details).
|
|
2432
|
+
|
|
2308
2433
|
### 11.13 Posted state from billing items (W-D3), `getPosted` / `getUnposted` / `postedWithin`, and drifted-create marker recovery (issues #10/#13/#237)
|
|
2309
2434
|
|
|
2310
2435
|
- **W-D3 (live, ww29, re-verified 2026-10-27)** — this zone's TimeEntry entity
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-autotask",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.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,
|