@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.
- package/README.md +208 -0
- package/dist/client.d.ts +48 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +252 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/datetime.d.ts +4 -0
- package/dist/datetime.d.ts.map +1 -0
- package/dist/datetime.js +64 -0
- package/dist/datetime.js.map +1 -0
- package/dist/format.d.ts +7 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +68 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/teamlink.d.ts +2 -0
- package/dist/teamlink.d.ts.map +1 -0
- package/dist/teamlink.js +22 -0
- package/dist/teamlink.js.map +1 -0
- package/dist/tools/alerts.d.ts +3 -0
- package/dist/tools/alerts.d.ts.map +1 -0
- package/dist/tools/alerts.js +78 -0
- package/dist/tools/alerts.js.map +1 -0
- package/dist/tools/auth.d.ts +3 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +24 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/devices.d.ts +3 -0
- package/dist/tools/devices.d.ts.map +1 -0
- package/dist/tools/devices.js +236 -0
- package/dist/tools/devices.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +13 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/organizations.d.ts +3 -0
- package/dist/tools/organizations.d.ts.map +1 -0
- package/dist/tools/organizations.js +101 -0
- package/dist/tools/organizations.js.map +1 -0
- package/dist/tools/queries.d.ts +3 -0
- package/dist/tools/queries.d.ts.map +1 -0
- package/dist/tools/queries.js +137 -0
- package/dist/tools/queries.js.map +1 -0
- package/dist/types.d.ts +243 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- 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
|
package/dist/client.d.ts
ADDED
|
@@ -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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"}
|
package/dist/config.d.ts
ADDED
|
@@ -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 @@
|
|
|
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"}
|