@teamnetwork-nz/ninjaone-mcp-server 0.1.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.
Files changed (54) hide show
  1. package/README.md +208 -0
  2. package/dist/client.d.ts +48 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +252 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/config.d.ts +10 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +58 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/datetime.d.ts +4 -0
  11. package/dist/datetime.d.ts.map +1 -0
  12. package/dist/datetime.js +64 -0
  13. package/dist/datetime.js.map +1 -0
  14. package/dist/format.d.ts +7 -0
  15. package/dist/format.d.ts.map +1 -0
  16. package/dist/format.js +68 -0
  17. package/dist/format.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +86 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/teamlink.d.ts +2 -0
  23. package/dist/teamlink.d.ts.map +1 -0
  24. package/dist/teamlink.js +22 -0
  25. package/dist/teamlink.js.map +1 -0
  26. package/dist/tools/alerts.d.ts +3 -0
  27. package/dist/tools/alerts.d.ts.map +1 -0
  28. package/dist/tools/alerts.js +78 -0
  29. package/dist/tools/alerts.js.map +1 -0
  30. package/dist/tools/auth.d.ts +3 -0
  31. package/dist/tools/auth.d.ts.map +1 -0
  32. package/dist/tools/auth.js +24 -0
  33. package/dist/tools/auth.js.map +1 -0
  34. package/dist/tools/devices.d.ts +3 -0
  35. package/dist/tools/devices.d.ts.map +1 -0
  36. package/dist/tools/devices.js +236 -0
  37. package/dist/tools/devices.js.map +1 -0
  38. package/dist/tools/index.d.ts +3 -0
  39. package/dist/tools/index.d.ts.map +1 -0
  40. package/dist/tools/index.js +13 -0
  41. package/dist/tools/index.js.map +1 -0
  42. package/dist/tools/organizations.d.ts +3 -0
  43. package/dist/tools/organizations.d.ts.map +1 -0
  44. package/dist/tools/organizations.js +101 -0
  45. package/dist/tools/organizations.js.map +1 -0
  46. package/dist/tools/queries.d.ts +3 -0
  47. package/dist/tools/queries.d.ts.map +1 -0
  48. package/dist/tools/queries.js +137 -0
  49. package/dist/tools/queries.js.map +1 -0
  50. package/dist/types.d.ts +243 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +3 -0
  53. package/dist/types.js.map +1 -0
  54. package/package.json +40 -0
package/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # @teamnetwork-nz/ninjaone-mcp-server
2
+
3
+ MCP server for [NinjaOne RMM](https://www.ninjaone.com/) — remote monitoring and
4
+ management via the **NinjaOne Public API v2**.
5
+
6
+ Compatible with Claude Desktop, Claude Code, Cursor, Cline, and any MCP-capable client.
7
+
8
+ **Phase 1 is read-only** and focuses on **client (organization) and machine (device)
9
+ information and stats**. See [SPEC.md](SPEC.md) for the full design and later phases.
10
+
11
+ ## Requirements
12
+
13
+ - Node.js 20+
14
+ - A NinjaOne API application (**Administration → Apps → API → Client App IDs**) using
15
+ platform **"API Services (machine-to-machine)"** with the **`monitoring`** scope.
16
+
17
+ ## NinjaOne app setup
18
+
19
+ Create the API application in the **same region** as your tenant (apps are region-local),
20
+ under **Administration → Apps → API → Client App IDs → Add**. You'll need NinjaOne admin
21
+ rights.
22
+
23
+ | Setting | Value | Why it matters |
24
+ |---|---|---|
25
+ | **Application Platform** | **API Services (machine-to-machine)** | Enables the `client_credentials` grant this server uses. Any other platform type won't authenticate. |
26
+ | **Redirect URI** | *(leave blank)* | Not used for machine-to-machine. |
27
+ | **Scopes** | **Monitoring** only | Read-only. Leaving Management/Control unticked is the server-side half of the read-only gate — nothing can write regardless of `NINJAONE_READWRITE`. |
28
+ | **Allowed Grant Types** | **Client Credentials** | Implied by the machine-to-machine platform. |
29
+
30
+ On save, **copy the Client Secret immediately** — NinjaOne shows it only once (lose it and
31
+ you must regenerate, which invalidates the old one). Put the Client ID / Secret and your
32
+ region into the environment variables below, then run `ninja_test_connection` — it echoes
33
+ the granted scope so you can confirm you got `monitoring` and nothing more.
34
+
35
+ **Tips**
36
+ - **One app per integration** so you can rotate or revoke this without affecting others.
37
+ - **Least privilege** — keep this app monitoring-only. For future write phases, create a
38
+ *separate* app with the `management` scope rather than widening this one.
39
+ - A machine-to-machine app has **tenant-wide visibility** (all organizations/devices);
40
+ there is no per-organization restriction on the app itself.
41
+
42
+ **Common setup errors**
43
+ - Token call fails with correct-looking credentials → wrong Application Platform, or the
44
+ app was created in a different region than `NINJAONE_REGION`.
45
+ - `404 / "Client app not exist"` → the Client ID doesn't exist in the target region
46
+ (suspect a region mismatch first).
47
+ - Data endpoints return `403` despite a valid token → the `monitoring` scope wasn't
48
+ granted; check the scope echoed by `ninja_test_connection`.
49
+
50
+ ## Configuration
51
+
52
+ Set these environment variables before starting the server.
53
+
54
+ ### Required
55
+
56
+ | Variable | Description |
57
+ |---|---|
58
+ | `NINJAONE_CLIENT_ID` | OAuth client id of the API application |
59
+ | `NINJAONE_CLIENT_SECRET` | OAuth client secret |
60
+ | `NINJAONE_REGION` | Region code selecting the base URL — one of `us`, `us2`, `eu`, `ca`, `oc`. **No default.** (May be replaced by `NINJAONE_BASE_URL`.) |
61
+
62
+ Region → base URL:
63
+
64
+ | Region | Base URL |
65
+ |---|---|
66
+ | `us` | `https://app.ninjarmm.com` |
67
+ | `us2` | `https://us2.ninjarmm.com` |
68
+ | `eu` | `https://eu.ninjarmm.com` |
69
+ | `ca` | `https://ca.ninjarmm.com` |
70
+ | `oc` (Oceania) | `https://oc.ninjarmm.com` |
71
+
72
+ ### Optional
73
+
74
+ | Variable | Default | Description |
75
+ |---|---|---|
76
+ | `NINJAONE_BASE_URL` | *(derived from region)* | Explicit base URL override |
77
+ | `NINJAONE_READWRITE` | `false` | `true` enables write helpers (Phase 1 ships no write tools) |
78
+ | `NINJAONE_DATETIMEFORMAT` | `local` | Timezone for date display — `local`, an IANA name (e.g. `Pacific/Auckland`), or `UTC` |
79
+ | `NINJAONE_PAGESIZE` | `100` | Default page size for list/query tools |
80
+
81
+ ## Claude Desktop configuration
82
+
83
+ ```json
84
+ {
85
+ "mcpServers": {
86
+ "ninjaone": {
87
+ "command": "npx",
88
+ "args": ["-y", "@teamnetwork-nz/ninjaone-mcp-server"],
89
+ "env": {
90
+ "NINJAONE_CLIENT_ID": "your-client-id",
91
+ "NINJAONE_CLIENT_SECRET": "your-client-secret",
92
+ "NINJAONE_REGION": "oc"
93
+ }
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## Claude Code configuration
100
+
101
+ ```bash
102
+ claude mcp add ninjaone \
103
+ -e NINJAONE_CLIENT_ID=your-client-id \
104
+ -e NINJAONE_CLIENT_SECRET=your-client-secret \
105
+ -e NINJAONE_REGION=oc \
106
+ -- npx -y @teamnetwork-nz/ninjaone-mcp-server
107
+ ```
108
+
109
+ ## Available tools (23, all read-only)
110
+
111
+ ### Connection
112
+ | Tool | Description |
113
+ |---|---|
114
+ | `ninja_test_connection` | Acquire a token and verify credentials; reports base URL, granted scope, and mode |
115
+
116
+ ### Organizations & locations
117
+ | Tool | Description |
118
+ |---|---|
119
+ | `ninja_list_organizations` | List client organizations (name-substring filter, pagination) |
120
+ | `ninja_get_organization` | Full org detail incl. locations |
121
+ | `ninja_list_locations` | List locations (all, or scoped to one org) |
122
+
123
+ ### Devices (machines)
124
+ | Tool | Description |
125
+ |---|---|
126
+ | `ninja_list_devices` | List machines — filter by org, location, class, online/offline, or raw `df` |
127
+ | `ninja_get_device` | Full device record: identity, OS, system, last contact/boot, IPs |
128
+ | `ninja_get_device_disks` | Physical disks — model, capacity, SMART |
129
+ | `ninja_get_device_volumes` | Logical volumes — capacity, free, % used |
130
+ | `ninja_get_device_processors` | CPUs — cores, clock |
131
+ | `ninja_get_device_network` | Network interfaces — IP/MAC/gateway/DNS |
132
+ | `ninja_get_device_software` | Installed software inventory |
133
+ | `ninja_get_device_os_patches` | OS patch state |
134
+ | `ninja_get_device_last_user` | Last logged-on user |
135
+ | `ninja_get_device_activities` | Recent device activity log |
136
+ | `ninja_get_device_custom_fields` | Custom field values |
137
+
138
+ ### Fleet stats (aggregate queries)
139
+ | Tool | Description |
140
+ |---|---|
141
+ | `ninja_query_device_health` | Health status across the fleet |
142
+ | `ninja_query_operating_systems` | OS distribution per device |
143
+ | `ninja_query_computer_systems` | Make/model/serial/RAM per device |
144
+ | `ninja_query_volumes` | Disk capacity/usage fleet-wide |
145
+ | `ninja_query_antivirus_status` | AV product & protection status |
146
+ | `ninja_query_os_patches` | Pending OS patches fleet-wide |
147
+
148
+ ### Alerts & activities
149
+ | Tool | Description |
150
+ |---|---|
151
+ | `ninja_list_alerts` | Currently triggered alerts (by device or org) |
152
+ | `ninja_list_activities` | Tenant-wide activity log |
153
+
154
+ ## Agent guidance
155
+
156
+ ### Hierarchy
157
+ Devices belong to an **Organization** (the client) and a **Location**. Call
158
+ `ninja_list_organizations` first to discover organization ids, then filter devices with
159
+ `organizationId`. Org/location ids are resolved to names automatically (cached in memory
160
+ for the process lifetime).
161
+
162
+ ### TEAMlinkID (CRM linkage)
163
+ `TEAMlinkID` is a derived field (not native to NinjaOne) for linking NinjaOne objects to
164
+ the internal **TEAMlink** CRM. It is the 5-digit integer beginning with `8` at the end of
165
+ the organisation name — e.g. `TEAMnetwork Systems - 81674` → `81674`. It appears on
166
+ `ninja_list_organizations`, `ninja_get_organization`, `ninja_list_devices`, and
167
+ `ninja_get_device`; organisations without such an id show it blank / `(none)`. Devices
168
+ inherit it from their organisation.
169
+
170
+ ### Device filtering
171
+ `ninja_list_devices` and the `ninja_query_*` tools accept `organizationId`,
172
+ `locationId`, `deviceClass`, `online`, or a raw NinjaOne device-filter via
173
+ `deviceFilter` (e.g. `class in (WINDOWS_SERVER) AND offline`).
174
+
175
+ ### Pagination
176
+ - List tools use cursor pagination — pass the reported `after=<id>`.
177
+ - `ninja_query_*` tools return a cursor name — pass it back as `cursor=<name>`.
178
+
179
+ ## Read-only mode (default)
180
+
181
+ Phase 1 registers **only** read tools. The gate is enforced in two layers:
182
+
183
+ 1. **Client guard** — `NINJAONE_READWRITE=false` (default) makes the HTTP client's
184
+ write helpers (`post/put/patch/delete`) throw before any request is sent.
185
+ 2. **OAuth scope** — provision the API app with only the `monitoring` scope, so even a
186
+ bypassed write would be rejected server-side.
187
+
188
+ ## Development
189
+
190
+ ```bash
191
+ npm install
192
+ npm run build
193
+
194
+ NINJAONE_CLIENT_ID=... \
195
+ NINJAONE_CLIENT_SECRET=... \
196
+ NINJAONE_REGION=oc \
197
+ node dist/index.js
198
+ ```
199
+
200
+ Test with the MCP inspector:
201
+
202
+ ```bash
203
+ npx @modelcontextprotocol/inspector node dist/index.js
204
+ ```
205
+
206
+ ## License
207
+
208
+ MIT
@@ -0,0 +1,48 @@
1
+ import { NinjaConfig } from './config.js';
2
+ export interface DeviceFilterOptions {
3
+ organizationId?: number | string;
4
+ locationId?: number | string;
5
+ deviceClass?: string;
6
+ online?: boolean;
7
+ raw?: string;
8
+ }
9
+ export interface PagingParams {
10
+ pageSize?: number;
11
+ after?: number | string;
12
+ }
13
+ export declare class NinjaClient {
14
+ private http;
15
+ private config;
16
+ private accessToken;
17
+ private tokenExpiresAt;
18
+ private grantedScope;
19
+ private orgNameCache;
20
+ private locationNameCache;
21
+ private deviceLabelCache;
22
+ constructor(config: NinjaConfig);
23
+ authenticate(): Promise<void>;
24
+ private ensureToken;
25
+ private authHeader;
26
+ getGrantedScope(): string | null;
27
+ getBaseUrl(): string;
28
+ getDefaultPageSize(): number;
29
+ private request;
30
+ isReadWrite(): boolean;
31
+ private assertWritePermitted;
32
+ get<T>(path: string, params?: Record<string, unknown>): Promise<T>;
33
+ post<T>(path: string, body: unknown): Promise<T>;
34
+ put<T>(path: string, body?: unknown): Promise<T>;
35
+ patch<T>(path: string, body: unknown): Promise<T>;
36
+ delete<T>(path: string): Promise<T>;
37
+ buildPaging(paging?: PagingParams): Record<string, unknown>;
38
+ buildDeviceFilter(opts?: DeviceFilterOptions): string | undefined;
39
+ private ensureOrgCache;
40
+ private ensureLocationCache;
41
+ resolveOrgName(id: number | undefined): Promise<string>;
42
+ resolveTeamlinkId(id: number | undefined): Promise<string>;
43
+ resolveLocationName(id: number | undefined): Promise<string>;
44
+ private ensureDeviceCache;
45
+ resolveDeviceLabel(id: number | undefined): Promise<string>;
46
+ clearNameCaches(): void;
47
+ }
48
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAAuB;IAG3C,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,iBAAiB,CAAoC;IAC7D,OAAO,CAAC,gBAAgB,CAAoC;gBAEhD,MAAM,EAAE,WAAW;IAUzB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YA2CrB,WAAW;IAMzB,OAAO,CAAC,UAAU;IAKlB,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,UAAU,IAAI,MAAM;IAIpB,kBAAkB,IAAI,MAAM;YAMd,OAAO;IA0CrB,WAAW,IAAI,OAAO;IAItB,OAAO,CAAC,oBAAoB;IAQtB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAKhD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAKhD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAKjD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAOzC,WAAW,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAS3D,iBAAiB,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS;YAenD,cAAc;YAed,mBAAmB;IAe3B,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IAQvD,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IAM1D,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;YAMpD,iBAAiB;IAgBzB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IAOjE,eAAe,IAAI,IAAI;CAKxB"}
package/dist/client.js ADDED
@@ -0,0 +1,252 @@
1
+ import axios from 'axios';
2
+ import { extractTeamlinkId } from './teamlink.js';
3
+ export class NinjaClient {
4
+ http;
5
+ config;
6
+ accessToken = null;
7
+ tokenExpiresAt = 0; // epoch millis
8
+ grantedScope = null;
9
+ // In-memory name caches, populated on first use (see resolveOrgName / resolveLocationName).
10
+ orgNameCache = null;
11
+ locationNameCache = null;
12
+ deviceLabelCache = null;
13
+ constructor(config) {
14
+ this.config = config;
15
+ this.http = axios.create({
16
+ baseURL: config.baseUrl,
17
+ headers: { 'Content-Type': 'application/json' },
18
+ });
19
+ }
20
+ // ---- Authentication -------------------------------------------------------
21
+ async authenticate() {
22
+ const body = new URLSearchParams({
23
+ grant_type: 'client_credentials',
24
+ client_id: this.config.clientId,
25
+ client_secret: this.config.clientSecret,
26
+ scope: this.config.scope,
27
+ });
28
+ let resp;
29
+ try {
30
+ resp = await this.http.post('/ws/oauth/token', body.toString(), {
31
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
32
+ });
33
+ }
34
+ catch (err) {
35
+ if (axios.isAxiosError(err)) {
36
+ const d = err.response?.data;
37
+ const detail = d?.['resultCode'] ??
38
+ d?.['error_description'] ??
39
+ d?.['error'] ??
40
+ err.message;
41
+ throw new Error(`NinjaOne authentication failed [${err.response?.status}]: ${detail}. ` +
42
+ `Check NINJAONE_CLIENT_ID / NINJAONE_CLIENT_SECRET / NINJAONE_REGION.`);
43
+ }
44
+ throw err;
45
+ }
46
+ const token = resp.data?.access_token;
47
+ if (!token) {
48
+ throw new Error('Authentication failed: no access_token in response');
49
+ }
50
+ this.accessToken = token;
51
+ this.grantedScope = resp.data.scope ?? null;
52
+ // Refresh 60s before actual expiry.
53
+ const ttlMs = (resp.data.expires_in ?? 3600) * 1000;
54
+ this.tokenExpiresAt = Date.now() + ttlMs - 60_000;
55
+ process.stderr.write(`[auth] token acquired — scope="${this.grantedScope ?? this.config.scope}", expires in ${Math.round(ttlMs / 1000)}s\n`);
56
+ }
57
+ async ensureToken() {
58
+ if (!this.accessToken || Date.now() >= this.tokenExpiresAt) {
59
+ await this.authenticate();
60
+ }
61
+ }
62
+ authHeader() {
63
+ if (!this.accessToken)
64
+ throw new Error('Not authenticated');
65
+ return { Authorization: `Bearer ${this.accessToken}` };
66
+ }
67
+ getGrantedScope() {
68
+ return this.grantedScope;
69
+ }
70
+ getBaseUrl() {
71
+ return this.config.baseUrl;
72
+ }
73
+ getDefaultPageSize() {
74
+ return this.config.pageSize;
75
+ }
76
+ // ---- Core request with 401 retry -----------------------------------------
77
+ async request(method, path, data, params, retry = true) {
78
+ await this.ensureToken();
79
+ const cfg = {
80
+ method,
81
+ url: path,
82
+ headers: this.authHeader(),
83
+ params,
84
+ data,
85
+ };
86
+ try {
87
+ const resp = await this.http.request(cfg);
88
+ return resp.data;
89
+ }
90
+ catch (err) {
91
+ if (axios.isAxiosError(err) && err.response?.status === 401 && retry) {
92
+ this.accessToken = null;
93
+ await this.authenticate();
94
+ return this.request(method, path, data, params, false);
95
+ }
96
+ if (axios.isAxiosError(err)) {
97
+ const respData = err.response?.data;
98
+ const msg = respData?.['error_description'] ??
99
+ respData?.['resultCode'] ??
100
+ respData?.['message'] ??
101
+ respData?.['error'] ??
102
+ err.message;
103
+ throw new Error(`NinjaOne API error [${err.response?.status}]: ${msg}`);
104
+ }
105
+ throw err;
106
+ }
107
+ }
108
+ // ---- Read-only gate -------------------------------------------------------
109
+ isReadWrite() {
110
+ return this.config.readWrite;
111
+ }
112
+ assertWritePermitted() {
113
+ if (!this.config.readWrite) {
114
+ throw new Error('This operation requires write access. Set NINJAONE_READWRITE=true to enable write operations.');
115
+ }
116
+ }
117
+ async get(path, params) {
118
+ return this.request('GET', path, undefined, params);
119
+ }
120
+ async post(path, body) {
121
+ this.assertWritePermitted();
122
+ return this.request('POST', path, body);
123
+ }
124
+ async put(path, body) {
125
+ this.assertWritePermitted();
126
+ return this.request('PUT', path, body ?? {});
127
+ }
128
+ async patch(path, body) {
129
+ this.assertWritePermitted();
130
+ return this.request('PATCH', path, body);
131
+ }
132
+ async delete(path) {
133
+ this.assertWritePermitted();
134
+ return this.request('DELETE', path);
135
+ }
136
+ // ---- Query helpers --------------------------------------------------------
137
+ buildPaging(paging) {
138
+ const p = {};
139
+ p['pageSize'] = Math.min(paging?.pageSize ?? this.config.pageSize, 1000);
140
+ if (paging?.after !== undefined)
141
+ p['after'] = paging.after;
142
+ return p;
143
+ }
144
+ // Compose a NinjaOne device-filter (df) string from convenience options plus an
145
+ // optional raw passthrough. Returns undefined when nothing is specified.
146
+ buildDeviceFilter(opts) {
147
+ if (!opts)
148
+ return undefined;
149
+ const clauses = [];
150
+ if (opts.organizationId !== undefined)
151
+ clauses.push(`org = ${opts.organizationId}`);
152
+ if (opts.locationId !== undefined)
153
+ clauses.push(`loc = ${opts.locationId}`);
154
+ if (opts.deviceClass)
155
+ clauses.push(`class = ${opts.deviceClass}`);
156
+ if (opts.online === true)
157
+ clauses.push('online');
158
+ if (opts.online === false)
159
+ clauses.push('offline');
160
+ if (opts.raw)
161
+ clauses.push(opts.raw);
162
+ if (!clauses.length)
163
+ return undefined;
164
+ return clauses.join(' AND ');
165
+ }
166
+ // ---- Org / location name resolution (cached) ------------------------------
167
+ async ensureOrgCache() {
168
+ if (this.orgNameCache)
169
+ return this.orgNameCache;
170
+ const cache = new Map();
171
+ try {
172
+ const orgs = await this.get('/v2/organizations', { pageSize: 1000 });
173
+ for (const o of orgs ?? []) {
174
+ if (o.id !== undefined)
175
+ cache.set(o.id, o.name ?? String(o.id));
176
+ }
177
+ }
178
+ catch {
179
+ // Leave cache empty on failure; callers fall back to raw ids.
180
+ }
181
+ this.orgNameCache = cache;
182
+ return cache;
183
+ }
184
+ async ensureLocationCache() {
185
+ if (this.locationNameCache)
186
+ return this.locationNameCache;
187
+ const cache = new Map();
188
+ try {
189
+ const locs = await this.get('/v2/locations', { pageSize: 1000 });
190
+ for (const l of locs ?? []) {
191
+ if (l.id !== undefined)
192
+ cache.set(l.id, l.name ?? String(l.id));
193
+ }
194
+ }
195
+ catch {
196
+ // ignore
197
+ }
198
+ this.locationNameCache = cache;
199
+ return cache;
200
+ }
201
+ async resolveOrgName(id) {
202
+ if (id === undefined)
203
+ return '';
204
+ const cache = await this.ensureOrgCache();
205
+ return cache.get(id) ?? String(id);
206
+ }
207
+ // Resolve the TEAMlinkID (CRM key) for an organisation id, derived from its name.
208
+ // Returns '' when the org has no TEAMlinkID in its name.
209
+ async resolveTeamlinkId(id) {
210
+ if (id === undefined)
211
+ return '';
212
+ const cache = await this.ensureOrgCache();
213
+ return extractTeamlinkId(cache.get(id)) ?? '';
214
+ }
215
+ async resolveLocationName(id) {
216
+ if (id === undefined)
217
+ return '';
218
+ const cache = await this.ensureLocationCache();
219
+ return cache.get(id) ?? String(id);
220
+ }
221
+ async ensureDeviceCache() {
222
+ if (this.deviceLabelCache)
223
+ return this.deviceLabelCache;
224
+ const cache = new Map();
225
+ try {
226
+ // Best-effort single large page; unresolved ids fall back to the raw id.
227
+ const devices = await this.get('/v2/devices', { pageSize: 1000 });
228
+ for (const d of devices ?? []) {
229
+ if (d.id !== undefined)
230
+ cache.set(d.id, d.displayName ?? d.systemName ?? d.dnsName ?? String(d.id));
231
+ }
232
+ }
233
+ catch {
234
+ // ignore
235
+ }
236
+ this.deviceLabelCache = cache;
237
+ return cache;
238
+ }
239
+ async resolveDeviceLabel(id) {
240
+ if (id === undefined)
241
+ return '';
242
+ const cache = await this.ensureDeviceCache();
243
+ return cache.get(id) ?? String(id);
244
+ }
245
+ // Drop cached names so the next resolve refetches (e.g. after a known change).
246
+ clearNameCaches() {
247
+ this.orgNameCache = null;
248
+ this.locationNameCache = null;
249
+ this.deviceLabelCache = null;
250
+ }
251
+ }
252
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface NinjaConfig {
2
+ baseUrl: string;
3
+ clientId: string;
4
+ clientSecret: string;
5
+ scope: string;
6
+ readWrite: boolean;
7
+ pageSize: number;
8
+ }
9
+ export declare function loadConfig(): NinjaConfig;
10
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAYD,wBAAgB,UAAU,IAAI,WAAW,CA8CxC"}
package/dist/config.js ADDED
@@ -0,0 +1,58 @@
1
+ // Region → base URL. There is intentionally NO default region: NINJAONE_REGION
2
+ // (or an explicit NINJAONE_BASE_URL) is a required configuration prerequisite.
3
+ const REGION_BASE_URLS = {
4
+ us: 'https://app.ninjarmm.com',
5
+ us2: 'https://us2.ninjarmm.com',
6
+ eu: 'https://eu.ninjarmm.com',
7
+ ca: 'https://ca.ninjarmm.com',
8
+ oc: 'https://oc.ninjarmm.com',
9
+ };
10
+ export function loadConfig() {
11
+ const clientId = process.env.NINJAONE_CLIENT_ID;
12
+ const clientSecret = process.env.NINJAONE_CLIENT_SECRET;
13
+ const region = process.env.NINJAONE_REGION?.trim().toLowerCase();
14
+ const explicitBaseUrl = process.env.NINJAONE_BASE_URL?.trim();
15
+ const missing = [];
16
+ if (!clientId)
17
+ missing.push('NINJAONE_CLIENT_ID');
18
+ if (!clientSecret)
19
+ missing.push('NINJAONE_CLIENT_SECRET');
20
+ if (!region && !explicitBaseUrl)
21
+ missing.push('NINJAONE_REGION (or NINJAONE_BASE_URL)');
22
+ if (missing.length > 0) {
23
+ throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
24
+ }
25
+ // Resolve base URL: explicit override wins, otherwise map the region code.
26
+ let baseUrl;
27
+ if (explicitBaseUrl) {
28
+ baseUrl = explicitBaseUrl;
29
+ }
30
+ else {
31
+ const mapped = REGION_BASE_URLS[region];
32
+ if (!mapped) {
33
+ throw new Error(`Unknown NINJAONE_REGION "${region}". Valid regions: ${Object.keys(REGION_BASE_URLS).join(', ')}. ` +
34
+ `Alternatively set NINJAONE_BASE_URL explicitly.`);
35
+ }
36
+ baseUrl = mapped;
37
+ }
38
+ const readWrite = process.env.NINJAONE_READWRITE?.toLowerCase() === 'true';
39
+ // Phase 1 is read-only: request only the "monitoring" scope. Write phases would add
40
+ // "management" when readWrite is enabled.
41
+ const scope = readWrite ? 'monitoring management' : 'monitoring';
42
+ const pageSize = clampPageSize(process.env.NINJAONE_PAGESIZE);
43
+ return {
44
+ baseUrl: baseUrl.replace(/\/$/, ''),
45
+ clientId: clientId,
46
+ clientSecret: clientSecret,
47
+ scope,
48
+ readWrite,
49
+ pageSize,
50
+ };
51
+ }
52
+ function clampPageSize(raw) {
53
+ const n = raw ? Number(raw) : 100;
54
+ if (!Number.isFinite(n) || n <= 0)
55
+ return 100;
56
+ return Math.min(Math.floor(n), 1000);
57
+ }
58
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AASA,+EAA+E;AAC/E,+EAA+E;AAC/E,MAAM,gBAAgB,GAA2B;IAC/C,EAAE,EAAE,0BAA0B;IAC9B,GAAG,EAAE,0BAA0B;IAC/B,EAAE,EAAE,yBAAyB;IAC7B,EAAE,EAAE,yBAAyB;IAC7B,EAAE,EAAE,yBAAyB;CAC9B,CAAC;AAEF,MAAM,UAAU,UAAU;IACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAChD,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;IACxD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACjE,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAE9D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAClD,IAAI,CAAC,YAAY;QAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe;QAAE,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IAExF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,2CAA2C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,2EAA2E;IAC3E,IAAI,OAAe,CAAC;IACpB,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,GAAG,eAAe,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAO,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,4BAA4B,MAAM,qBAAqB,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;gBACjG,iDAAiD,CACpD,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,WAAW,EAAE,KAAK,MAAM,CAAC;IAE3E,oFAAoF;IACpF,0CAA0C;IAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,YAAY,CAAC;IAEjE,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAE9D,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACnC,QAAQ,EAAE,QAAS;QACnB,YAAY,EAAE,YAAa;QAC3B,KAAK;QACL,SAAS;QACT,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,GAAuB;IAC5C,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function getConfiguredTz(): string;
2
+ export declare function fromUtc(value: string | number | null | undefined): string;
3
+ export declare function maybeFromUtc(val: unknown): unknown;
4
+ //# sourceMappingURL=datetime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datetime.d.ts","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"AASA,wBAAgB,eAAe,IAAI,MAAM,CAGxC;AAID,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAuBzE;AAqBD,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGlD"}