node-autotask 0.30.0 → 0.32.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 +39 -0
- package/MCP_TOOL_CATALOG.json +6 -6
- package/MCP_TOOL_MANIFEST.md +26 -28
- package/MCP_TOOL_OVERRIDES.json +0 -12
- package/N8N_MCP_MANIFEST.json +8 -8
- package/N8N_MCP_MANIFEST.md +8 -8
- package/README.md +7 -5
- package/capabilities.json +1473 -1449
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-EDVSWL32.cjs → chunk-DCTHO7CQ.cjs} +1472 -1448
- package/dist/{chunk-RS7QMPRO.cjs → chunk-FTF2FEQ6.cjs} +526 -490
- package/dist/{chunk-P4PC32RJ.js → chunk-FVHPN7ZR.js} +61 -25
- package/dist/{chunk-LLIB4JGK.cjs → chunk-IKPFR6K2.cjs} +6 -6
- package/dist/{chunk-TZC4ZAA6.js → chunk-M2EGFPGF.js} +1472 -1448
- package/dist/{chunk-L4TDVUJM.cjs → chunk-QCB4LYKD.cjs} +143 -143
- package/dist/{chunk-QQPCJT2L.js → chunk-R6HLJCI2.js} +3 -3
- package/dist/{chunk-2ETR5GSO.js → chunk-T6EDJRLT.js} +2 -2
- package/dist/{index-B9VNv-EP.d.cts → index-Cw4aNNMo.d.ts} +65 -14
- package/dist/{index-DkpaFqyu.d.ts → index-OkbCWcef.d.cts} +65 -14
- package/dist/{index-CJ36PwRG.d.cts → index-m-7fOwFl.d.cts} +1 -1
- package/dist/{index-CJ36PwRG.d.ts → index-m-7fOwFl.d.ts} +1 -1
- package/dist/index.cjs +22 -22
- package/dist/index.d.cts +52 -5
- package/dist/index.d.ts +52 -5
- package/dist/index.js +4 -4
- package/dist/operations/index.cjs +112 -112
- package/dist/operations/index.d.cts +4 -4
- package/dist/operations/index.d.ts +4 -4
- package/dist/operations/index.js +2 -2
- package/dist/pool.cjs +9 -9
- package/dist/pool.d.cts +2 -2
- package/dist/pool.d.ts +2 -2
- package/dist/pool.js +4 -4
- package/dist/resources/index.cjs +4 -4
- package/dist/resources/index.d.cts +2 -2
- package/dist/resources/index.d.ts +2 -2
- package/dist/resources/index.js +3 -3
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/docs/API.md +61 -36
- package/package.json +1 -1
|
@@ -40,10 +40,10 @@ import {
|
|
|
40
40
|
unwrapItems,
|
|
41
41
|
unwrapOperation,
|
|
42
42
|
vendorId
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-R6HLJCI2.js";
|
|
44
44
|
import {
|
|
45
45
|
CAPABILITIES
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-M2EGFPGF.js";
|
|
47
47
|
|
|
48
48
|
// src/resources/appointments.ts
|
|
49
49
|
var AppointmentsResource = class extends BaseResource {
|
|
@@ -446,7 +446,7 @@ function dateOnlyWindowBounds(kind, now, tz) {
|
|
|
446
446
|
}
|
|
447
447
|
async function dateWindowQuery(http, path, field, kind, limit, operation, now, tz) {
|
|
448
448
|
const zone = tz === void 0 ? void 0 : validateZone(tz, operation);
|
|
449
|
-
const { from, to } =
|
|
449
|
+
const { from, to } = dateOnlyWindowBounds(kind, now, zone);
|
|
450
450
|
const filter = [
|
|
451
451
|
{ field, op: "gte", value: from },
|
|
452
452
|
{ field, op: "lte", value: to }
|
|
@@ -484,15 +484,16 @@ function customRangeDay(value, label, zone, opName) {
|
|
|
484
484
|
return { y, mo, d, ms };
|
|
485
485
|
}
|
|
486
486
|
async function customRangeQuery(http, path, field, from, to, limit, operation, zone) {
|
|
487
|
-
|
|
488
|
-
const
|
|
487
|
+
if (zone !== void 0) validateZone(zone, operation);
|
|
488
|
+
const fromB = customRangeDay(from, "from", void 0, operation);
|
|
489
|
+
const toB = customRangeDay(to, "to", void 0, operation);
|
|
489
490
|
if (fromB.ms > toB.ms) {
|
|
490
491
|
throw new AutotaskConfigError(
|
|
491
492
|
`${operation}: from (${isoUtc(fromB.ms)}) must not be after to (${isoUtc(toB.ms)})`,
|
|
492
493
|
{ operation }
|
|
493
494
|
);
|
|
494
495
|
}
|
|
495
|
-
const toEndMs =
|
|
496
|
+
const toEndMs = toB.ms + DAY_MS - 1;
|
|
496
497
|
const filter = [
|
|
497
498
|
{ field, op: "gte", value: isoUtc(fromB.ms) },
|
|
498
499
|
{ field, op: "lte", value: isoUtc(toEndMs) }
|
|
@@ -12575,6 +12576,33 @@ var TicketWebhooksResource = class extends BaseResource {
|
|
|
12575
12576
|
}
|
|
12576
12577
|
};
|
|
12577
12578
|
|
|
12579
|
+
// src/resources/special/time_entries/posted.ts
|
|
12580
|
+
var isApproved = (entry) => typeof entry.billingApprovalDateTime === "string" && entry.billingApprovalDateTime.trim() !== "";
|
|
12581
|
+
async function postedStateOf(http, entries, op) {
|
|
12582
|
+
const states = /* @__PURE__ */ new Map();
|
|
12583
|
+
const ids2 = [...new Set(entries.map((e) => e.id))];
|
|
12584
|
+
let incomplete = false;
|
|
12585
|
+
for (let c = 0; c < ids2.length; c += FETCH_BY_IDS_CHUNK_SIZE) {
|
|
12586
|
+
const chunk = ids2.slice(c, c + FETCH_BY_IDS_CHUNK_SIZE);
|
|
12587
|
+
const res = await http.request({
|
|
12588
|
+
method: "POST",
|
|
12589
|
+
idempotent: true,
|
|
12590
|
+
path: "/V1.0/BillingItems/query",
|
|
12591
|
+
body: { MaxRecords: 500, IncludeFields: ["id", "timeEntryID"], filter: [{ field: "timeEntryID", op: "in", value: chunk }] },
|
|
12592
|
+
operation: op
|
|
12593
|
+
});
|
|
12594
|
+
const { items, pageDetails } = unwrapItems(res);
|
|
12595
|
+
const billed = new Set(items.map((i) => i.timeEntryID));
|
|
12596
|
+
const more = pageDetails.nextPageUrl != null;
|
|
12597
|
+
if (more) incomplete = true;
|
|
12598
|
+
for (const id of chunk) {
|
|
12599
|
+
if (billed.has(id)) states.set(id, "posted");
|
|
12600
|
+
else if (!more) states.set(id, "unposted");
|
|
12601
|
+
}
|
|
12602
|
+
}
|
|
12603
|
+
return { states, incomplete };
|
|
12604
|
+
}
|
|
12605
|
+
|
|
12578
12606
|
// src/resources/special/time_entries/windows.ts
|
|
12579
12607
|
var DAY_MS2 = 864e5;
|
|
12580
12608
|
var iso2 = (ms) => new Date(ms).toISOString().slice(0, 19) + "Z";
|
|
@@ -12615,14 +12643,23 @@ async function customRange3(_resource, from, to, http, opts) {
|
|
|
12615
12643
|
const { items } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), "time_entries.customRange", http);
|
|
12616
12644
|
return items;
|
|
12617
12645
|
}
|
|
12618
|
-
async function
|
|
12619
|
-
const filter = [
|
|
12620
|
-
{ field: "postedDate", op: mode === "posted" ? "exist" : "notExist" }
|
|
12621
|
-
];
|
|
12622
|
-
if (opts?.ticketID !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
|
|
12646
|
+
async function postedStateQuery(_resource, mode, http, opts) {
|
|
12623
12647
|
const operation = mode === "posted" ? "time_entries.getPosted" : "time_entries.getUnposted";
|
|
12624
|
-
const
|
|
12625
|
-
|
|
12648
|
+
const window = { from: null, to: null };
|
|
12649
|
+
const filter = [];
|
|
12650
|
+
const fromB = opts?.dateFrom === void 0 ? void 0 : customRangeDay(opts.dateFrom, "from", void 0, operation);
|
|
12651
|
+
const toB = opts?.dateTo === void 0 ? void 0 : customRangeDay(opts.dateTo, "to", void 0, operation);
|
|
12652
|
+
if (fromB && toB && fromB.ms > toB.ms) {
|
|
12653
|
+
throw new AutotaskConfigError(`${operation}: dateFrom (${iso2(fromB.ms)}) must not be after dateTo (${iso2(toB.ms)})`, { operation });
|
|
12654
|
+
}
|
|
12655
|
+
if (fromB) filter.push({ field: "dateWorked", op: "gte", value: window.from = iso2(fromB.ms) });
|
|
12656
|
+
if (toB) filter.push({ field: "dateWorked", op: "lte", value: window.to = iso2(toB.ms + DAY_MS2 - 1) });
|
|
12657
|
+
if (opts?.ticketID !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
|
|
12658
|
+
const read = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), operation, http);
|
|
12659
|
+
const { states, incomplete } = await postedStateOf(http, read.items, operation);
|
|
12660
|
+
const items = read.items.filter((e) => states.get(e.id) === mode);
|
|
12661
|
+
const approvedCount = items.filter(isApproved).length;
|
|
12662
|
+
return { items, hasMore: read.hasMore || incomplete, window, approvedCount, unapprovedCount: items.length - approvedCount };
|
|
12626
12663
|
}
|
|
12627
12664
|
async function postedWithin(_resource, from, to, http, opts, opLabel = "time_entries.postedWithin") {
|
|
12628
12665
|
const OP11 = opLabel;
|
|
@@ -12637,26 +12674,25 @@ async function postedWithin(_resource, from, to, http, opts, opLabel = "time_ent
|
|
|
12637
12674
|
{ field: "dateWorked", op: "gte", value: iso2(fromB.ms) },
|
|
12638
12675
|
{ field: "dateWorked", op: "lte", value: iso2(windowToMs) }
|
|
12639
12676
|
];
|
|
12640
|
-
if (opts?.posted === true) filter.push({ field: "postedDate", op: "exist" });
|
|
12641
|
-
else if (opts?.posted === false) filter.push({ field: "postedDate", op: "notExist" });
|
|
12642
12677
|
if (opts?.ticketID !== void 0) filter.push({ field: "ticketID", op: "eq", value: opts.ticketID });
|
|
12643
12678
|
if (opts?.resourceID !== void 0) filter.push({ field: "resourceID", op: "eq", value: opts.resourceID });
|
|
12644
12679
|
const { items, hasMore } = await queryTimeEntries(_resource, filter, clampWindowLimit(opts?.limit), OP11, http);
|
|
12680
|
+
const { states, incomplete } = await postedStateOf(http, items, OP11);
|
|
12645
12681
|
let postedCount = 0;
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
if (
|
|
12649
|
-
|
|
12650
|
-
}
|
|
12682
|
+
let unpostedCount = 0;
|
|
12683
|
+
for (const state of states.values()) {
|
|
12684
|
+
if (state === "posted") postedCount += 1;
|
|
12685
|
+
else unpostedCount += 1;
|
|
12651
12686
|
}
|
|
12687
|
+
const keep = opts?.posted === void 0 ? void 0 : opts.posted ? "posted" : "unposted";
|
|
12652
12688
|
return {
|
|
12653
|
-
items,
|
|
12689
|
+
items: keep === void 0 ? items : items.filter((e) => states.get(e.id) === keep),
|
|
12654
12690
|
postedCount,
|
|
12655
|
-
unpostedCount
|
|
12691
|
+
unpostedCount,
|
|
12656
12692
|
window: { from: iso2(fromB.ms), to: iso2(windowToMs) },
|
|
12657
12693
|
// A live next-page cursor past the bounded read: the counts above cover
|
|
12658
12694
|
// the returned rows ONLY when this is true (issue #66 AC1 — the 500-row cap case is the general case).
|
|
12659
|
-
truncated: hasMore
|
|
12695
|
+
truncated: hasMore || incomplete
|
|
12660
12696
|
};
|
|
12661
12697
|
}
|
|
12662
12698
|
async function dateWorkedWindow(resource, from, to, http, opts) {
|
|
@@ -13411,11 +13447,11 @@ var TimeEntriesResource = class extends BaseResource {
|
|
|
13411
13447
|
}
|
|
13412
13448
|
/** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
|
|
13413
13449
|
getPosted(opts) {
|
|
13414
|
-
return
|
|
13450
|
+
return postedStateQuery(this, "posted", this.http, opts);
|
|
13415
13451
|
}
|
|
13416
13452
|
/** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
|
|
13417
13453
|
getUnposted(opts) {
|
|
13418
|
-
return
|
|
13454
|
+
return postedStateQuery(this, "unposted", this.http, opts);
|
|
13419
13455
|
}
|
|
13420
13456
|
/** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
|
|
13421
13457
|
last14(opts) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8;
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkDCTHO7CQcjs = require('./chunk-DCTHO7CQ.cjs');
|
|
4
4
|
|
|
5
5
|
// src/auth.ts
|
|
6
6
|
var AUTH_HEADERS = {
|
|
@@ -10863,8 +10863,8 @@ var EMPTY_REASON_STAMPS = {
|
|
|
10863
10863
|
var POINTER_NOTES = {
|
|
10864
10864
|
"ticket_history.query": `W1 (ww29 (australia-nz) live 2026-10-04; vendor contract change live-verified 2026-10-06): the vendor requires ticket_history.query to be supplied with exactly ONE ticketID-equals filter \u2014 an unfiltered call 500s by contract (10-06 text: "When querying the TicketHistory entity you are required to supply one and only one ticketID equals filter"). WITH the filter the query WORKS (live 2026-10-06: rows returned, hasMore true \u2014 a single {op:'eq', field:'ticketID', value:<id>} condition; the filter vocabulary is eq/noteq/gt/gte/lt/lte/beginsWith/endsWith/contains/exist/notExist/in/notIn/and/or). tickets.timeline / tickets.fullContext remain the convenient composites; raw history rows come from the filtered query.`,
|
|
10865
10865
|
"time_entries.create": `W-A3 (ww29 (australia-nz) live 2026-10-04; re-walled 2026-10-06; scope re-stamped 2026-10-21): for a time_entries.create whose (resourceID, roleID) pair selects the API-only user's own resource, the vendor refused with "API-only user cannot be selected for this field" (10-04 text: "The specified AssignedResourceID and AssignedRoleID combination is not currently defined"). This is a (resource, role) pair wall, NOT a blanket refusal of time-entry writes: live time_entries.create calls succeeded on 2026-10-21 (pair not recorded; two entries created, read back, 1.0 h total). The authorised pair set has NOT been re-adjudicated by a live probe; if a create fails with the pair-validation text, check the resource's role authorisation (whoAmI / the resource record) and try another pair. createForResource's 10-06 wall is likewise unconfirmed for other pairs. Working reads remain postedWithin / the window helpers (W-D3 pointer note).`,
|
|
10866
|
-
"time_entries.getPosted":
|
|
10867
|
-
"time_entries.getUnposted":
|
|
10866
|
+
"time_entries.getPosted": `W-D3 (ww29 (australia-nz) live 2026-10-04, re-verified live 2026-10-27): this zone's TimeEntry entity exposes NO posted-state field \u2014 reads carry no postedDate/postedOnTime and a postedDate exist/notExist filter fails with SERVER_ERROR "Unable to find postedDate in the TimeEntry Entity". time_entries.getPosted therefore no longer sends any postedDate condition: posted state is derived from billing_items.timeEntryID (an entry is posted when a billing item references it, whether or not billingApprovalDateTime is set \u2014 ww29 never populates it). If a call still fails, the fault is NOT the old postedDate wall: it is a billing_items join/read fault (typed error \u2014 429 RATE_LIMITED included \u2014 propagates, no partial result). The dateWorked window reads time_entries.postedWithin / time_entries.dateWorkedWindow use the same derivation.`,
|
|
10867
|
+
"time_entries.getUnposted": `W-D3 (ww29 (australia-nz) live 2026-10-04, re-verified live 2026-10-27): this zone's TimeEntry entity exposes NO posted-state field \u2014 reads carry no postedDate/postedOnTime and a postedDate exist/notExist filter fails with SERVER_ERROR "Unable to find postedDate in the TimeEntry Entity". time_entries.getUnposted therefore no longer sends any postedDate condition: unposted state is derived from billing_items.timeEntryID (an entry is posted when a billing item references it, whether or not billingApprovalDateTime is set \u2014 ww29 never populates it). If a call still fails, the fault is NOT the old postedDate wall: it is a billing_items join/read fault (typed error \u2014 429 RATE_LIMITED included \u2014 propagates, no partial result). The dateWorked window reads time_entries.postedWithin / time_entries.dateWorkedWindow use the same derivation.`,
|
|
10868
10868
|
"authenticate.get": "W2: authenticate.get is DESIGNATOR-ONLY \u2014 it returns the designator identity, not a tenant data surface; a miss/empty here is not a data-empty signal (no emptyReason is attached by design \u2014 a false reason would be worse than none)"
|
|
10869
10869
|
};
|
|
10870
10870
|
var BLIND_SURFACE_INVENTORY = {
|
|
@@ -17031,7 +17031,7 @@ function buildEntityTypeMap() {
|
|
|
17031
17031
|
}
|
|
17032
17032
|
var ENTITY_TYPE_TO_RESOURCE = buildEntityTypeMap();
|
|
17033
17033
|
var ENUMERABLE_RESOURCES = new Set(
|
|
17034
|
-
|
|
17034
|
+
_chunkDCTHO7CQcjs.CAPABILITIES.filter((rec) => rec.resource !== "operations" && (rec.operation === "query" || rec.operation === "get")).map(
|
|
17035
17035
|
(rec) => rec.resource
|
|
17036
17036
|
)
|
|
17037
17037
|
);
|
|
@@ -17108,7 +17108,7 @@ function capabilitySets() {
|
|
|
17108
17108
|
const fieldOps = /* @__PURE__ */ new Map();
|
|
17109
17109
|
const udfOps = /* @__PURE__ */ new Map();
|
|
17110
17110
|
const resources = /* @__PURE__ */ new Set();
|
|
17111
|
-
for (const rec of
|
|
17111
|
+
for (const rec of _chunkDCTHO7CQcjs.CAPABILITIES) {
|
|
17112
17112
|
if (rec.resource === "operations") continue;
|
|
17113
17113
|
resources.add(rec.resource);
|
|
17114
17114
|
if (rec.operation === "getFieldInfo") fieldOps.set(rec.resource, rec);
|
|
@@ -17120,7 +17120,7 @@ var derivedIndex = null;
|
|
|
17120
17120
|
function derivedFieldsFor(resource) {
|
|
17121
17121
|
if (derivedIndex === null) {
|
|
17122
17122
|
derivedIndex = /* @__PURE__ */ new Map();
|
|
17123
|
-
for (const rec of
|
|
17123
|
+
for (const rec of _chunkDCTHO7CQcjs.CAPABILITIES) {
|
|
17124
17124
|
const declared = _optionalChain([rec, 'access', _127 => _127.metadata, 'optionalAccess', _128 => _128.derivedFields]);
|
|
17125
17125
|
if (!Array.isArray(declared) || derivedIndex.has(rec.resource)) continue;
|
|
17126
17126
|
const rows = [];
|