@arbium/enterprise-agent-connect 0.0.0-stage → 0.1.2-dev.9022f22

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 (322) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +269 -2
  3. package/dist/connectors/dell-techdirect/connector.json +39 -0
  4. package/dist/connectors/dell-techdirect/index.d.ts +8 -0
  5. package/dist/connectors/dell-techdirect/index.js +7 -0
  6. package/dist/connectors/dell-techdirect/index.js.map +1 -0
  7. package/dist/connectors/dell-techdirect/tools.d.ts +2 -0
  8. package/dist/connectors/dell-techdirect/tools.js +49 -0
  9. package/dist/connectors/dell-techdirect/tools.js.map +1 -0
  10. package/dist/connectors/dell-techdirect/vendor.d.ts +6 -0
  11. package/dist/connectors/dell-techdirect/vendor.js +57 -0
  12. package/dist/connectors/dell-techdirect/vendor.js.map +1 -0
  13. package/dist/connectors/dynamics-365/connector.json +228 -0
  14. package/dist/connectors/dynamics-365/index.d.ts +8 -0
  15. package/dist/connectors/dynamics-365/index.js +10 -0
  16. package/dist/connectors/dynamics-365/index.js.map +1 -0
  17. package/dist/connectors/dynamics-365/tools.d.ts +35 -0
  18. package/dist/connectors/dynamics-365/tools.js +806 -0
  19. package/dist/connectors/dynamics-365/tools.js.map +1 -0
  20. package/dist/connectors/dynamics-365/vendor.d.ts +9 -0
  21. package/dist/connectors/dynamics-365/vendor.js +110 -0
  22. package/dist/connectors/dynamics-365/vendor.js.map +1 -0
  23. package/dist/connectors/entra/connector.json +85 -0
  24. package/dist/connectors/entra/index.d.ts +2 -0
  25. package/dist/connectors/entra/index.js +4 -0
  26. package/dist/connectors/entra/index.js.map +1 -0
  27. package/dist/connectors/entra/tools.d.ts +14 -0
  28. package/dist/connectors/entra/tools.js +262 -0
  29. package/dist/connectors/entra/tools.js.map +1 -0
  30. package/dist/connectors/fedex/connector.json +118 -0
  31. package/dist/connectors/fedex/index.d.ts +8 -0
  32. package/dist/connectors/fedex/index.js +7 -0
  33. package/dist/connectors/fedex/index.js.map +1 -0
  34. package/dist/connectors/fedex/tools.d.ts +3 -0
  35. package/dist/connectors/fedex/tools.js +152 -0
  36. package/dist/connectors/fedex/tools.js.map +1 -0
  37. package/dist/connectors/fedex/vendor.d.ts +7 -0
  38. package/dist/connectors/fedex/vendor.js +98 -0
  39. package/dist/connectors/fedex/vendor.js.map +1 -0
  40. package/dist/connectors/gong/connector.json +127 -0
  41. package/dist/connectors/gong/index.d.ts +8 -0
  42. package/dist/connectors/gong/index.js +7 -0
  43. package/dist/connectors/gong/index.js.map +1 -0
  44. package/dist/connectors/gong/tools.d.ts +8 -0
  45. package/dist/connectors/gong/tools.js +424 -0
  46. package/dist/connectors/gong/tools.js.map +1 -0
  47. package/dist/connectors/gong/vendor.d.ts +4 -0
  48. package/dist/connectors/gong/vendor.js +89 -0
  49. package/dist/connectors/gong/vendor.js.map +1 -0
  50. package/dist/connectors/http/index.d.ts +34 -0
  51. package/dist/connectors/http/index.js +325 -0
  52. package/dist/connectors/http/index.js.map +1 -0
  53. package/dist/connectors/intune/connector.json +106 -0
  54. package/dist/connectors/intune/index.d.ts +2 -0
  55. package/dist/connectors/intune/index.js +4 -0
  56. package/dist/connectors/intune/index.js.map +1 -0
  57. package/dist/connectors/intune/tools.d.ts +12 -0
  58. package/dist/connectors/intune/tools.js +416 -0
  59. package/dist/connectors/intune/tools.js.map +1 -0
  60. package/dist/connectors/jamf/connector.json +182 -0
  61. package/dist/connectors/jamf/index.d.ts +8 -0
  62. package/dist/connectors/jamf/index.js +7 -0
  63. package/dist/connectors/jamf/index.js.map +1 -0
  64. package/dist/connectors/jamf/tools.d.ts +7 -0
  65. package/dist/connectors/jamf/tools.js +659 -0
  66. package/dist/connectors/jamf/tools.js.map +1 -0
  67. package/dist/connectors/jamf/vendor.d.ts +5 -0
  68. package/dist/connectors/jamf/vendor.js +70 -0
  69. package/dist/connectors/jamf/vendor.js.map +1 -0
  70. package/dist/connectors/jira/connector.json +272 -0
  71. package/dist/connectors/jira/index.d.ts +8 -0
  72. package/dist/connectors/jira/index.js +7 -0
  73. package/dist/connectors/jira/index.js.map +1 -0
  74. package/dist/connectors/jira/jql.d.ts +4 -0
  75. package/dist/connectors/jira/jql.js +67 -0
  76. package/dist/connectors/jira/jql.js.map +1 -0
  77. package/dist/connectors/jira/tools.d.ts +3 -0
  78. package/dist/connectors/jira/tools.js +464 -0
  79. package/dist/connectors/jira/tools.js.map +1 -0
  80. package/dist/connectors/jira/vendor.d.ts +3 -0
  81. package/dist/connectors/jira/vendor.js +100 -0
  82. package/dist/connectors/jira/vendor.js.map +1 -0
  83. package/dist/connectors/microsoft/client.d.ts +66 -0
  84. package/dist/connectors/microsoft/client.js +452 -0
  85. package/dist/connectors/microsoft/client.js.map +1 -0
  86. package/dist/connectors/microsoft/provider.json +118 -0
  87. package/dist/connectors/netsuite/connector.json +317 -0
  88. package/dist/connectors/netsuite/index.d.ts +8 -0
  89. package/dist/connectors/netsuite/index.js +7 -0
  90. package/dist/connectors/netsuite/index.js.map +1 -0
  91. package/dist/connectors/netsuite/tools.d.ts +24 -0
  92. package/dist/connectors/netsuite/tools.js +447 -0
  93. package/dist/connectors/netsuite/tools.js.map +1 -0
  94. package/dist/connectors/netsuite/vendor.d.ts +26 -0
  95. package/dist/connectors/netsuite/vendor.js +247 -0
  96. package/dist/connectors/netsuite/vendor.js.map +1 -0
  97. package/dist/connectors/odata/index.d.ts +33 -0
  98. package/dist/connectors/odata/index.js +460 -0
  99. package/dist/connectors/odata/index.js.map +1 -0
  100. package/dist/connectors/okta/connector.json +194 -0
  101. package/dist/connectors/okta/index.d.ts +8 -0
  102. package/dist/connectors/okta/index.js +7 -0
  103. package/dist/connectors/okta/index.js.map +1 -0
  104. package/dist/connectors/okta/tools.d.ts +6 -0
  105. package/dist/connectors/okta/tools.js +429 -0
  106. package/dist/connectors/okta/tools.js.map +1 -0
  107. package/dist/connectors/okta/vendor.d.ts +14 -0
  108. package/dist/connectors/okta/vendor.js +204 -0
  109. package/dist/connectors/okta/vendor.js.map +1 -0
  110. package/dist/connectors/outlook-calendar/connector.json +136 -0
  111. package/dist/connectors/outlook-calendar/index.d.ts +2 -0
  112. package/dist/connectors/outlook-calendar/index.js +4 -0
  113. package/dist/connectors/outlook-calendar/index.js.map +1 -0
  114. package/dist/connectors/outlook-calendar/tools.d.ts +65 -0
  115. package/dist/connectors/outlook-calendar/tools.js +398 -0
  116. package/dist/connectors/outlook-calendar/tools.js.map +1 -0
  117. package/dist/connectors/outlook-mail/connector.json +154 -0
  118. package/dist/connectors/outlook-mail/index.d.ts +2 -0
  119. package/dist/connectors/outlook-mail/index.js +4 -0
  120. package/dist/connectors/outlook-mail/index.js.map +1 -0
  121. package/dist/connectors/outlook-mail/tools.d.ts +8 -0
  122. package/dist/connectors/outlook-mail/tools.js +236 -0
  123. package/dist/connectors/outlook-mail/tools.js.map +1 -0
  124. package/dist/connectors/salesforce/connector.json +122 -0
  125. package/dist/connectors/salesforce/index.d.ts +8 -0
  126. package/dist/connectors/salesforce/index.js +7 -0
  127. package/dist/connectors/salesforce/index.js.map +1 -0
  128. package/dist/connectors/salesforce/soql.d.ts +3 -0
  129. package/dist/connectors/salesforce/soql.js +60 -0
  130. package/dist/connectors/salesforce/soql.js.map +1 -0
  131. package/dist/connectors/salesforce/tools.d.ts +9 -0
  132. package/dist/connectors/salesforce/tools.js +234 -0
  133. package/dist/connectors/salesforce/tools.js.map +1 -0
  134. package/dist/connectors/salesforce/vendor.d.ts +3 -0
  135. package/dist/connectors/salesforce/vendor.js +113 -0
  136. package/dist/connectors/salesforce/vendor.js.map +1 -0
  137. package/dist/connectors/sap-s4hana/connector.json +264 -0
  138. package/dist/connectors/sap-s4hana/index.d.ts +8 -0
  139. package/dist/connectors/sap-s4hana/index.js +7 -0
  140. package/dist/connectors/sap-s4hana/index.js.map +1 -0
  141. package/dist/connectors/sap-s4hana/tools.d.ts +4 -0
  142. package/dist/connectors/sap-s4hana/tools.js +761 -0
  143. package/dist/connectors/sap-s4hana/tools.js.map +1 -0
  144. package/dist/connectors/sap-s4hana/vendor.d.ts +5 -0
  145. package/dist/connectors/sap-s4hana/vendor.js +176 -0
  146. package/dist/connectors/sap-s4hana/vendor.js.map +1 -0
  147. package/dist/connectors/servicenow/connector.json +228 -0
  148. package/dist/connectors/servicenow/index.d.ts +8 -0
  149. package/dist/connectors/servicenow/index.js +7 -0
  150. package/dist/connectors/servicenow/index.js.map +1 -0
  151. package/dist/connectors/servicenow/tools.d.ts +4 -0
  152. package/dist/connectors/servicenow/tools.js +884 -0
  153. package/dist/connectors/servicenow/tools.js.map +1 -0
  154. package/dist/connectors/servicenow/vendor.d.ts +3 -0
  155. package/dist/connectors/servicenow/vendor.js +106 -0
  156. package/dist/connectors/servicenow/vendor.js.map +1 -0
  157. package/dist/connectors/sharepoint-files/connector.json +194 -0
  158. package/dist/connectors/sharepoint-files/excel.d.ts +16 -0
  159. package/dist/connectors/sharepoint-files/excel.js +300 -0
  160. package/dist/connectors/sharepoint-files/excel.js.map +1 -0
  161. package/dist/connectors/sharepoint-files/index.d.ts +8 -0
  162. package/dist/connectors/sharepoint-files/index.js +12 -0
  163. package/dist/connectors/sharepoint-files/index.js.map +1 -0
  164. package/dist/connectors/sharepoint-files/tools.d.ts +52 -0
  165. package/dist/connectors/sharepoint-files/tools.js +365 -0
  166. package/dist/connectors/sharepoint-files/tools.js.map +1 -0
  167. package/dist/connectors/sharepoint-files/vendor.d.ts +11 -0
  168. package/dist/connectors/sharepoint-files/vendor.js +167 -0
  169. package/dist/connectors/sharepoint-files/vendor.js.map +1 -0
  170. package/dist/connectors/slack/connector.json +148 -0
  171. package/dist/connectors/slack/index.d.ts +8 -0
  172. package/dist/connectors/slack/index.js +7 -0
  173. package/dist/connectors/slack/index.js.map +1 -0
  174. package/dist/connectors/slack/tools.d.ts +3 -0
  175. package/dist/connectors/slack/tools.js +355 -0
  176. package/dist/connectors/slack/tools.js.map +1 -0
  177. package/dist/connectors/slack/vendor.d.ts +17 -0
  178. package/dist/connectors/slack/vendor.js +189 -0
  179. package/dist/connectors/slack/vendor.js.map +1 -0
  180. package/dist/connectors/soap/index.d.ts +16 -0
  181. package/dist/connectors/soap/index.js +248 -0
  182. package/dist/connectors/soap/index.js.map +1 -0
  183. package/dist/connectors/soap/xml.d.ts +12 -0
  184. package/dist/connectors/soap/xml.js +126 -0
  185. package/dist/connectors/soap/xml.js.map +1 -0
  186. package/dist/connectors/sql/engines.d.ts +58 -0
  187. package/dist/connectors/sql/engines.js +292 -0
  188. package/dist/connectors/sql/engines.js.map +1 -0
  189. package/dist/connectors/sql/index.d.ts +16 -0
  190. package/dist/connectors/sql/index.js +214 -0
  191. package/dist/connectors/sql/index.js.map +1 -0
  192. package/dist/connectors/teams/connector.json +298 -0
  193. package/dist/connectors/teams/index.d.ts +2 -0
  194. package/dist/connectors/teams/index.js +5 -0
  195. package/dist/connectors/teams/index.js.map +1 -0
  196. package/dist/connectors/teams/tools.d.ts +7 -0
  197. package/dist/connectors/teams/tools.js +392 -0
  198. package/dist/connectors/teams/tools.js.map +1 -0
  199. package/dist/connectors/workday/connector.json +188 -0
  200. package/dist/connectors/workday/index.d.ts +8 -0
  201. package/dist/connectors/workday/index.js +7 -0
  202. package/dist/connectors/workday/index.js.map +1 -0
  203. package/dist/connectors/workday/tools.d.ts +33 -0
  204. package/dist/connectors/workday/tools.js +410 -0
  205. package/dist/connectors/workday/tools.js.map +1 -0
  206. package/dist/connectors/workday/vendor.d.ts +18 -0
  207. package/dist/connectors/workday/vendor.js +199 -0
  208. package/dist/connectors/workday/vendor.js.map +1 -0
  209. package/dist/packages/connector-kit/src/branding.d.ts +57 -0
  210. package/dist/packages/connector-kit/src/branding.js +102 -0
  211. package/dist/packages/connector-kit/src/branding.js.map +1 -0
  212. package/dist/packages/connector-kit/src/certificate.d.ts +19 -0
  213. package/dist/packages/connector-kit/src/certificate.js +84 -0
  214. package/dist/packages/connector-kit/src/certificate.js.map +1 -0
  215. package/dist/packages/connector-kit/src/common.d.ts +36 -0
  216. package/dist/packages/connector-kit/src/common.js +110 -0
  217. package/dist/packages/connector-kit/src/common.js.map +1 -0
  218. package/dist/packages/connector-kit/src/connector.d.ts +1356 -0
  219. package/dist/packages/connector-kit/src/connector.js +1073 -0
  220. package/dist/packages/connector-kit/src/connector.js.map +1 -0
  221. package/dist/packages/connector-kit/src/fetch.d.ts +6 -0
  222. package/dist/packages/connector-kit/src/fetch.js +44 -0
  223. package/dist/packages/connector-kit/src/fetch.js.map +1 -0
  224. package/dist/packages/connector-kit/src/index.d.ts +5 -0
  225. package/dist/packages/connector-kit/src/index.js +6 -0
  226. package/dist/packages/connector-kit/src/index.js.map +1 -0
  227. package/dist/packages/connector-kit/src/oauth.d.ts +15 -0
  228. package/dist/packages/connector-kit/src/oauth.js +98 -0
  229. package/dist/packages/connector-kit/src/oauth.js.map +1 -0
  230. package/dist/packages/connector-kit/src/types.d.ts +87 -0
  231. package/dist/packages/connector-kit/src/types.js +20 -0
  232. package/dist/packages/connector-kit/src/types.js.map +1 -0
  233. package/dist/packages/server/migrations/0001_tables.sql +268 -0
  234. package/dist/packages/server/migrations/0002_organizations.sql +72 -0
  235. package/dist/packages/server/migrations/0003_drop_unused.sql +3 -0
  236. package/dist/packages/server/migrations/0004_trigger_runners.sql +15 -0
  237. package/dist/packages/server/migrations/0005_run_runner.sql +3 -0
  238. package/dist/packages/server/migrations/0006_approvals.sql +36 -0
  239. package/dist/packages/server/migrations/0007_app_events.sql +110 -0
  240. package/dist/packages/server/migrations/rollback/to-legacy.sql +90 -0
  241. package/dist/packages/server/src/api.d.ts +3730 -0
  242. package/dist/packages/server/src/api.js +820 -0
  243. package/dist/packages/server/src/api.js.map +1 -0
  244. package/dist/packages/server/src/app.d.ts +23 -0
  245. package/dist/packages/server/src/app.js +467 -0
  246. package/dist/packages/server/src/app.js.map +1 -0
  247. package/dist/packages/server/src/builtin.d.ts +5 -0
  248. package/dist/packages/server/src/builtin.js +42 -0
  249. package/dist/packages/server/src/builtin.js.map +1 -0
  250. package/dist/packages/server/src/consent.d.ts +27 -0
  251. package/dist/packages/server/src/consent.js +36 -0
  252. package/dist/packages/server/src/consent.js.map +1 -0
  253. package/dist/packages/server/src/events/admin.d.ts +156 -0
  254. package/dist/packages/server/src/events/admin.js +255 -0
  255. package/dist/packages/server/src/events/admin.js.map +1 -0
  256. package/dist/packages/server/src/events/deliver.d.ts +43 -0
  257. package/dist/packages/server/src/events/deliver.js +129 -0
  258. package/dist/packages/server/src/events/deliver.js.map +1 -0
  259. package/dist/packages/server/src/events/runtime.d.ts +28 -0
  260. package/dist/packages/server/src/events/runtime.js +400 -0
  261. package/dist/packages/server/src/events/runtime.js.map +1 -0
  262. package/dist/packages/server/src/events/store.d.ts +172 -0
  263. package/dist/packages/server/src/events/store.js +275 -0
  264. package/dist/packages/server/src/events/store.js.map +1 -0
  265. package/dist/packages/server/src/index.d.ts +40 -0
  266. package/dist/packages/server/src/index.js +186 -0
  267. package/dist/packages/server/src/index.js.map +1 -0
  268. package/dist/packages/server/src/migrate.d.ts +11 -0
  269. package/dist/packages/server/src/migrate.js +134 -0
  270. package/dist/packages/server/src/migrate.js.map +1 -0
  271. package/dist/packages/server/src/openapi.d.ts +4 -0
  272. package/dist/packages/server/src/openapi.js +101 -0
  273. package/dist/packages/server/src/openapi.js.map +1 -0
  274. package/dist/packages/server/src/queue.d.ts +10 -0
  275. package/dist/packages/server/src/queue.js +45 -0
  276. package/dist/packages/server/src/queue.js.map +1 -0
  277. package/dist/packages/server/src/secrets.d.ts +19 -0
  278. package/dist/packages/server/src/secrets.js +76 -0
  279. package/dist/packages/server/src/secrets.js.map +1 -0
  280. package/dist/packages/server/src/server.d.ts +1 -0
  281. package/dist/packages/server/src/server.js +82 -0
  282. package/dist/packages/server/src/server.js.map +1 -0
  283. package/dist/packages/server/src/service.d.ts +767 -0
  284. package/dist/packages/server/src/service.js +924 -0
  285. package/dist/packages/server/src/service.js.map +1 -0
  286. package/dist/packages/server/src/store.d.ts +157 -0
  287. package/dist/packages/server/src/store.js +415 -0
  288. package/dist/packages/server/src/store.js.map +1 -0
  289. package/dist/packages/server/src/teams/activity.d.ts +123 -0
  290. package/dist/packages/server/src/teams/activity.js +188 -0
  291. package/dist/packages/server/src/teams/activity.js.map +1 -0
  292. package/dist/packages/server/src/teams/admin.d.ts +335 -0
  293. package/dist/packages/server/src/teams/admin.js +699 -0
  294. package/dist/packages/server/src/teams/admin.js.map +1 -0
  295. package/dist/packages/server/src/teams/approval.d.ts +57 -0
  296. package/dist/packages/server/src/teams/approval.js +118 -0
  297. package/dist/packages/server/src/teams/approval.js.map +1 -0
  298. package/dist/packages/server/src/teams/bot.d.ts +25 -0
  299. package/dist/packages/server/src/teams/bot.js +124 -0
  300. package/dist/packages/server/src/teams/bot.js.map +1 -0
  301. package/dist/packages/server/src/teams/claude.d.ts +46 -0
  302. package/dist/packages/server/src/teams/claude.js +165 -0
  303. package/dist/packages/server/src/teams/claude.js.map +1 -0
  304. package/dist/packages/server/src/teams/jwt.d.ts +17 -0
  305. package/dist/packages/server/src/teams/jwt.js +121 -0
  306. package/dist/packages/server/src/teams/jwt.js.map +1 -0
  307. package/dist/packages/server/src/teams/package.d.ts +79 -0
  308. package/dist/packages/server/src/teams/package.js +319 -0
  309. package/dist/packages/server/src/teams/package.js.map +1 -0
  310. package/dist/packages/server/src/teams/provision.d.ts +84 -0
  311. package/dist/packages/server/src/teams/provision.js +505 -0
  312. package/dist/packages/server/src/teams/provision.js.map +1 -0
  313. package/dist/packages/server/src/teams/runner.d.ts +80 -0
  314. package/dist/packages/server/src/teams/runner.js +187 -0
  315. package/dist/packages/server/src/teams/runner.js.map +1 -0
  316. package/dist/packages/server/src/teams/runtime.d.ts +91 -0
  317. package/dist/packages/server/src/teams/runtime.js +679 -0
  318. package/dist/packages/server/src/teams/runtime.js.map +1 -0
  319. package/dist/packages/server/src/teams/store.d.ts +233 -0
  320. package/dist/packages/server/src/teams/store.js +507 -0
  321. package/dist/packages/server/src/teams/store.js.map +1 -0
  322. package/package.json +68 -4
@@ -0,0 +1,7 @@
1
+ import { type Toolset } from "../../packages/connector-kit/src/common.js";
2
+ export declare const MAX_SCOPED_PAGES = 5;
3
+ export declare const MAX_POLICY_READS = 50;
4
+ /** Jamf Pro API errors are {httpStatus, errors[{code, description}]}; the Classic API answers
5
+ * an HTML page. */
6
+ export declare function jamfError(data: unknown): string;
7
+ export declare const jamfTools: Toolset;
@@ -0,0 +1,659 @@
1
+ import { z } from "zod";
2
+ import { vendorError } from "../../packages/connector-kit/src/oauth.js";
3
+ import { audited, Denied, json, listLimit, VendorFailed, } from "../../packages/connector-kit/src/common.js";
4
+ const ID = /^[0-9]{1,10}$/;
5
+ // Letters, digits, spaces and . _ @ + ' -: nothing that could end an RSQL value or add a clause.
6
+ const SEARCH = /^[\p{L}\p{N} ._@+'-]{1,100}$/u;
7
+ const PIN = /^[0-9]{6}$/;
8
+ const OFFSET = /^[0-9]{1,6}$/;
9
+ // Under a sites limit a listing reads on, at most this many pages, until it has max_results.
10
+ export const MAX_SCOPED_PAGES = 5;
11
+ // ponytail: under a sites limit each listed policy is read for its site; at most this many per
12
+ // call. A site-aware policy listing if Jamf adds one.
13
+ export const MAX_POLICY_READS = 50;
14
+ const MAX_APPS = 500;
15
+ const LABEL = { computer: "computer", mobile: "mobile device" };
16
+ const INVENTORY = {
17
+ computer: {
18
+ path: "/api/v4/computers-inventory",
19
+ id: "id",
20
+ sort: "general.name:asc",
21
+ sections: ["GENERAL", "HARDWARE", "OPERATING_SYSTEM", "USER_AND_LOCATION"],
22
+ groups: "GROUP_MEMBERSHIPS",
23
+ search: (s) => `general.name=="*${s}*",hardware.serialNumber=="${s}",userAndLocation.username=="${s}",userAndLocation.email=="${s}"`,
24
+ },
25
+ mobile: {
26
+ path: "/api/v2/mobile-devices/detail",
27
+ id: "mobileDeviceId",
28
+ sort: "displayName:asc",
29
+ sections: ["GENERAL", "HARDWARE", "USER_AND_LOCATION", "SECURITY"],
30
+ groups: "GROUPS",
31
+ search: (s) => `displayName=="*${s}*",serialNumber=="${s}",username=="${s}",emailAddress=="${s}"`,
32
+ },
33
+ };
34
+ const GROUP_LISTS = {
35
+ computer: {
36
+ static: "/api/v3/computer-groups/static-groups",
37
+ smart: "/api/v3/computer-groups/smart-groups",
38
+ name: "name",
39
+ },
40
+ mobile: {
41
+ static: "/api/v2/mobile-device-groups/static-groups",
42
+ smart: "/api/v2/mobile-device-groups/smart-groups",
43
+ name: "groupName",
44
+ },
45
+ };
46
+ const CLASSIC_GROUP = {
47
+ computer: {
48
+ path: "/JSSResource/computergroups/id",
49
+ root: "computer_group",
50
+ members: "computers",
51
+ item: "computer",
52
+ },
53
+ mobile: {
54
+ path: "/JSSResource/mobiledevicegroups/id",
55
+ root: "mobile_device_group",
56
+ members: "mobile_devices",
57
+ item: "mobile_device",
58
+ },
59
+ };
60
+ /** Jamf Pro API errors are {httpStatus, errors[{code, description}]}; the Classic API answers
61
+ * an HTML page. */
62
+ export function jamfError(data) {
63
+ const errors = data?.errors;
64
+ if (Array.isArray(errors))
65
+ return errors
66
+ .map((e) => e.description || e.code)
67
+ .filter(Boolean)
68
+ .join("; ")
69
+ .slice(0, 300);
70
+ if (typeof data === "string")
71
+ return data
72
+ .replace(/<[^>]*>/g, " ")
73
+ .replace(/\s+/g, " ")
74
+ .trim()
75
+ .slice(0, 300);
76
+ return vendorError(data);
77
+ }
78
+ async function ok(ctx, mark, req, hintTool) {
79
+ const res = await ctx.call(req);
80
+ mark(res.status);
81
+ if (res.status < 200 || res.status >= 300) {
82
+ const detail = jamfError(res.data);
83
+ throw new VendorFailed(res.status, `Jamf Pro answered ${res.status}${detail ? `: ${detail}` : ""}`, hintTool);
84
+ }
85
+ return res.data;
86
+ }
87
+ function keySites(ctx) {
88
+ const sites = listLimit(ctx.limits, "sites");
89
+ return sites ? new Set(sites) : null;
90
+ }
91
+ const inSites = (sites, site) => !sites || (site !== undefined && site !== null && sites.has(String(site)));
92
+ const siteOf = (kind, d) => kind === "computer" ? d.general?.site?.id : d.general?.siteId;
93
+ const deviceId = (kind, d) => String(kind === "computer" ? d.id : d.mobileDeviceId);
94
+ function summary(kind, d) {
95
+ if (kind === "computer") {
96
+ const c = d;
97
+ return {
98
+ id: c.id,
99
+ kind,
100
+ name: c.general?.name ?? null,
101
+ serial_number: c.hardware?.serialNumber ?? null,
102
+ model: c.hardware?.model ?? null,
103
+ os: c.operatingSystem?.name ?? null,
104
+ os_version: c.operatingSystem?.version ?? null,
105
+ user: c.userAndLocation?.username || null,
106
+ user_name: c.userAndLocation?.realname || null,
107
+ email: c.userAndLocation?.email || null,
108
+ last_contact: c.general?.lastContact ?? null,
109
+ last_inventory: c.general?.reportDate ?? null,
110
+ managed: c.general?.remoteManagement?.managed ?? null,
111
+ supervised: c.general?.supervised ?? null,
112
+ filevault: c.operatingSystem?.fileVault2Status ?? null,
113
+ site_id: c.general?.site?.id ?? null,
114
+ };
115
+ }
116
+ const m = d;
117
+ return {
118
+ id: m.mobileDeviceId,
119
+ kind,
120
+ type: m.deviceType ?? null,
121
+ name: m.general?.displayName ?? null,
122
+ serial_number: m.hardware?.serialNumber ?? null,
123
+ model: m.hardware?.model ?? null,
124
+ os_version: m.general?.osVersion ?? null,
125
+ user: m.userAndLocation?.username || null,
126
+ user_name: m.userAndLocation?.realName || null,
127
+ email: m.userAndLocation?.emailAddress || null,
128
+ last_contact: m.general?.lastContactDate ?? null,
129
+ last_inventory: m.general?.lastInventoryUpdateDate ?? null,
130
+ managed: m.general?.managed ?? null,
131
+ supervised: m.general?.supervised ?? null,
132
+ encrypted: m.security?.dataProtected ?? null,
133
+ passcode: m.security?.passcodePresent ?? null,
134
+ lost_mode: m.security?.lostModeEnabled ?? null,
135
+ site_id: m.general?.siteId ?? null,
136
+ };
137
+ }
138
+ function inventoryPath(kind, sections, page, size, filter) {
139
+ const q = new URLSearchParams();
140
+ for (const s of sections)
141
+ q.append("section", s);
142
+ q.set("page", String(page));
143
+ q.set("page-size", String(size));
144
+ q.set("sort", INVENTORY[kind].sort);
145
+ if (filter)
146
+ q.set("filter", filter);
147
+ return `${INVENTORY[kind].path}?${q}`;
148
+ }
149
+ /** Pages of a Jamf Pro API listing from `page`, read on (under a sites limit) until `want`
150
+ * rows are kept or MAX_SCOPED_PAGES were read. */
151
+ async function scan(ctx, mark, pathFor, keep, want, page) {
152
+ const rows = [];
153
+ let total = 0;
154
+ for (let read = 0; read < MAX_SCOPED_PAGES; read++) {
155
+ const data = await ok(ctx, mark, { method: "GET", path: pathFor(page) });
156
+ const results = data.results ?? [];
157
+ total = data.totalCount ?? 0;
158
+ rows.push(...results.filter(keep));
159
+ page++;
160
+ if (rows.length >= want || page * want >= total || !results.length)
161
+ break;
162
+ }
163
+ return { rows, total, next: page * want < total ? String(page) : null };
164
+ }
165
+ /** The devices with these ids, by id; ids Jamf does not know are missing. */
166
+ async function readDevices(ctx, mark, kind, ids, sections = INVENTORY[kind].sections, hintTool) {
167
+ if (!ids.length)
168
+ return new Map();
169
+ const data = await ok(ctx, mark, {
170
+ method: "GET",
171
+ path: inventoryPath(kind, sections, 0, ids.length, `${INVENTORY[kind].id}=in=(${ids.join(",")})`),
172
+ }, hintTool);
173
+ return new Map((data.results ?? []).map((d) => [deviceId(kind, d), d]));
174
+ }
175
+ /** One device, read and checked against the key's sites. A device Jamf does not know and one
176
+ * outside the sites get the same refusal, so a kept key cannot learn which ids exist.
177
+ * `hintTool`: the read is the always-on read capability's permission. */
178
+ async function openDevice(ctx, mark, kind, id, sections = INVENTORY[kind].sections) {
179
+ if (!ID.test(id))
180
+ throw new Denied(`Give a ${LABEL[kind]} id from jamf_search_devices`);
181
+ const device = (await readDevices(ctx, mark, kind, [id], sections, "jamf_get_device")).get(id);
182
+ if (!device || !inSites(keySites(ctx), siteOf(kind, device)))
183
+ throw new Denied(`No ${LABEL[kind]} ${id} that this agent may use`);
184
+ return device;
185
+ }
186
+ /** Several devices, each checked as openDevice does; refuses naming every one that is not. */
187
+ async function openDevices(ctx, mark, kind, ids) {
188
+ const bad = ids.find((id) => !ID.test(id));
189
+ if (bad !== undefined)
190
+ throw new Denied(`${bad} is not a ${LABEL[kind]} id`);
191
+ const sites = keySites(ctx);
192
+ const found = await readDevices(ctx, mark, kind, ids, ["GENERAL"], "jamf_get_device");
193
+ const refused = ids.filter((id) => {
194
+ const d = found.get(id);
195
+ return !d || !inSites(sites, siteOf(kind, d));
196
+ });
197
+ if (refused.length)
198
+ throw new Denied(`No ${LABEL[kind]}s ${refused.join(", ")} that this agent may use`);
199
+ return found;
200
+ }
201
+ async function openGroup(ctx, mark, kind, id) {
202
+ if (!ID.test(id))
203
+ throw new Denied("Give a group id from jamf_list_groups");
204
+ const g = CLASSIC_GROUP[kind];
205
+ const refused = new Denied(`No ${LABEL[kind]} group ${id} that this agent may use`);
206
+ const res = await ctx.call({ method: "GET", path: `${g.path}/${id}` });
207
+ mark(res.status);
208
+ if (res.status === 404)
209
+ throw refused;
210
+ if (res.status !== 200) {
211
+ const detail = jamfError(res.data);
212
+ throw new VendorFailed(res.status, `Jamf Pro answered ${res.status}${detail ? `: ${detail}` : ""}`);
213
+ }
214
+ const group = res.data[g.root];
215
+ if (!group || !inSites(keySites(ctx), group.site?.id))
216
+ throw refused;
217
+ return group;
218
+ }
219
+ const groupMembers = (kind, group) => (group[CLASSIC_GROUP[kind].members] ?? []).map((m) => String(m.id));
220
+ const offset = (cursor) => {
221
+ if (cursor === undefined)
222
+ return 0;
223
+ if (!OFFSET.test(cursor))
224
+ throw new Denied("Give the `next` value from the previous call");
225
+ return Number(cursor);
226
+ };
227
+ function managementId(kind, d) {
228
+ const id = d.general?.managementId;
229
+ if (!id)
230
+ throw new Denied(`Jamf Pro has no management id for ${LABEL[kind]} ${deviceId(kind, d)}: it is not enrolled in MDM, so it cannot take commands.`);
231
+ return id;
232
+ }
233
+ async function mdmCommand(ctx, mark, kind, device, commandData) {
234
+ const queued = await ok(ctx, mark, {
235
+ method: "POST",
236
+ path: "/api/v2/mdm/commands",
237
+ body: { clientData: [{ managementId: managementId(kind, device) }], commandData },
238
+ });
239
+ const s = summary(kind, device);
240
+ return json({
241
+ device_id: s.id,
242
+ name: s.name,
243
+ command: commandData.commandType,
244
+ command_id: queued?.[0]?.id ?? null,
245
+ status: "queued",
246
+ note: "Jamf Pro sent the command; the device carries it out when it next checks in.",
247
+ });
248
+ }
249
+ const TRIGGERS = [
250
+ "trigger_checkin",
251
+ "trigger_enrollment_complete",
252
+ "trigger_login",
253
+ "trigger_logout",
254
+ "trigger_network_state_changed",
255
+ "trigger_startup",
256
+ ];
257
+ async function readPolicy(ctx, mark, id, subset = "") {
258
+ const refused = new Denied(`No policy ${id} that this agent may use`);
259
+ const res = await ctx.call({ method: "GET", path: `/JSSResource/policies/id/${id}${subset}` });
260
+ mark(res.status);
261
+ if (res.status === 404)
262
+ throw refused;
263
+ if (res.status !== 200) {
264
+ const detail = jamfError(res.data);
265
+ throw new VendorFailed(res.status, `Jamf Pro answered ${res.status}${detail ? `: ${detail}` : ""}`);
266
+ }
267
+ const policy = res.data.policy;
268
+ if (!policy)
269
+ throw refused;
270
+ return { policy, inScope: inSites(keySites(ctx), policy.general?.site?.id) };
271
+ }
272
+ export const jamfTools = {
273
+ names: [
274
+ "jamf_search_devices",
275
+ "jamf_get_device",
276
+ "jamf_list_groups",
277
+ "jamf_get_group",
278
+ "jamf_change_group_members",
279
+ "jamf_send_blank_push",
280
+ "jamf_restart_device",
281
+ "jamf_lock_device",
282
+ "jamf_erase_device",
283
+ "jamf_list_policies",
284
+ "jamf_get_policy",
285
+ "jamf_computer_policy_logs",
286
+ ],
287
+ register(server, ctx, allowed) {
288
+ const readOnly = { readOnlyHint: true, openWorldHint: true };
289
+ const kind = z
290
+ .enum(["computer", "mobile"])
291
+ .describe("computer: Macs. mobile: iPhones, iPads, Apple TVs and other mobile devices.");
292
+ const id = z.string().max(10).describe("A device id, from jamf_search_devices.");
293
+ const cursor = z
294
+ .string()
295
+ .max(10)
296
+ .optional()
297
+ .describe("The `next` value from the previous call with the same arguments.");
298
+ if (allowed.has("jamf_search_devices"))
299
+ server.registerTool("jamf_search_devices", {
300
+ description: "Find computers or mobile devices in Jamf Pro: by part of the device name, or by exact serial number, username or email. Without `search`, lists them all by name. Each has its id, serial number, model, system version, user, last contact and inventory time, whether it is managed and supervised, and FileVault (computers) or encryption, passcode and Lost Mode (mobile devices). When `next` is set, call again with `cursor` set to it.",
301
+ inputSchema: z.object({
302
+ kind,
303
+ search: z.string().max(100).optional(),
304
+ max_results: z.number().int().min(1).max(100).default(25),
305
+ cursor,
306
+ }),
307
+ annotations: readOnly,
308
+ }, ({ kind, search, max_results, cursor }) => audited(ctx, "jamf_search_devices", `${kind} ${search ?? ""}`.trim(), async (mark) => {
309
+ const s = search?.trim();
310
+ if (s && !SEARCH.test(s))
311
+ throw new Denied("Search with letters, digits, spaces and . _ @ + ' - only (a name, serial number, username or email)");
312
+ const sites = keySites(ctx);
313
+ const found = await scan(ctx, mark, (page) => inventoryPath(kind, INVENTORY[kind].sections, page, max_results, s ? INVENTORY[kind].search(s) : undefined), (d) => inSites(sites, siteOf(kind, d)), max_results, offset(cursor));
314
+ return json({
315
+ devices: found.rows.map((d) => summary(kind, d)),
316
+ next: found.next,
317
+ });
318
+ }));
319
+ if (allowed.has("jamf_get_device"))
320
+ server.registerTool("jamf_get_device", {
321
+ description: "One computer or mobile device: everything jamf_search_devices shows, plus when it last enrolled and the groups it is in. With `include_apps`, also the apps installed on it (name, version, bundle id), at most 500.",
322
+ inputSchema: z.object({
323
+ kind,
324
+ device_id: id,
325
+ include_apps: z.boolean().default(false),
326
+ }),
327
+ annotations: readOnly,
328
+ }, ({ kind, device_id, include_apps }) => audited(ctx, "jamf_get_device", `${kind} ${device_id}`, async (mark) => {
329
+ const sections = [
330
+ ...INVENTORY[kind].sections,
331
+ INVENTORY[kind].groups,
332
+ ...(include_apps ? ["APPLICATIONS"] : []),
333
+ ];
334
+ const d = await openDevice(ctx, mark, kind, device_id, sections);
335
+ const groups = kind === "computer"
336
+ ? (d.groupMemberships ?? []).map((g) => ({
337
+ id: g.groupId ?? null,
338
+ name: g.groupName ?? null,
339
+ smart: g.smartGroup ?? null,
340
+ }))
341
+ : (d.groups ?? []).map((g) => ({
342
+ id: g.groupId ?? null,
343
+ name: g.groupName ?? null,
344
+ smart: g.smart ?? null,
345
+ }));
346
+ const apps = !include_apps
347
+ ? undefined
348
+ : kind === "computer"
349
+ ? (d.applications ?? []).map((a) => ({
350
+ name: a.name ?? null,
351
+ version: a.version ?? null,
352
+ bundle_id: a.bundleId ?? null,
353
+ }))
354
+ : (d.applications ?? []).map((a) => ({
355
+ name: a.name ?? null,
356
+ version: a.shortVersion || a.version || null,
357
+ bundle_id: a.identifier ?? null,
358
+ }));
359
+ return json({
360
+ ...summary(kind, d),
361
+ ...(kind === "computer" ? { site: d.general?.site?.name ?? null } : {}),
362
+ last_enrolled: d.general?.lastEnrolledDate ?? null,
363
+ groups,
364
+ ...(apps ? { apps: apps.slice(0, MAX_APPS), apps_total: apps.length } : {}),
365
+ });
366
+ }));
367
+ if (allowed.has("jamf_list_groups"))
368
+ server.registerTool("jamf_list_groups", {
369
+ description: "List computer or mobile device groups: static groups (devices added by hand) or smart groups (devices matching criteria), with id, name, description, site and member count. Optionally only names containing `search`. When `next` is set, call again with `cursor` set to it.",
370
+ inputSchema: z.object({
371
+ kind,
372
+ type: z.enum(["static", "smart"]),
373
+ search: z.string().max(100).optional(),
374
+ max_results: z.number().int().min(1).max(100).default(50),
375
+ cursor,
376
+ }),
377
+ annotations: readOnly,
378
+ }, ({ kind, type, search, max_results, cursor }) => audited(ctx, "jamf_list_groups", `${kind} ${type} ${search ?? ""}`.trim(), async (mark) => {
379
+ const s = search?.trim();
380
+ if (s && !SEARCH.test(s))
381
+ throw new Denied("Search with letters, digits, spaces and . _ @ + ' - only");
382
+ const list = GROUP_LISTS[kind];
383
+ const sites = keySites(ctx);
384
+ const found = await scan(ctx, mark, (page) => {
385
+ const q = new URLSearchParams({
386
+ page: String(page),
387
+ "page-size": String(max_results),
388
+ sort: `${list.name}:asc`,
389
+ });
390
+ if (s)
391
+ q.set("filter", `${list.name}=="*${s}*"`);
392
+ return `${list[type]}?${q}`;
393
+ }, (g) => inSites(sites, g.siteId), max_results, offset(cursor));
394
+ return json({
395
+ groups: found.rows.map((g) => ({
396
+ id: g.id ?? g.groupId ?? null,
397
+ name: g.name ?? g.groupName ?? null,
398
+ description: (g.description ?? g.groupDescription) || null,
399
+ site_id: g.siteId ?? null,
400
+ members: g.count ?? g.membershipCount ?? null,
401
+ })),
402
+ next: found.next,
403
+ });
404
+ }));
405
+ if (allowed.has("jamf_get_group"))
406
+ server.registerTool("jamf_get_group", {
407
+ description: "One computer or mobile device group: whether it is smart or static, its site and its members (as jamf_search_devices shows them). When `next` is set, call again with `cursor` set to it.",
408
+ inputSchema: z.object({
409
+ kind,
410
+ group_id: z.string().max(10).describe("A group id, from jamf_list_groups."),
411
+ max_results: z.number().int().min(1).max(100).default(50),
412
+ cursor,
413
+ }),
414
+ annotations: readOnly,
415
+ }, ({ kind, group_id, max_results, cursor }) => audited(ctx, "jamf_get_group", `${kind} group ${group_id}`, async (mark) => {
416
+ const group = await openGroup(ctx, mark, kind, group_id);
417
+ const ids = groupMembers(kind, group);
418
+ const start = offset(cursor);
419
+ const window = ids.slice(start, start + max_results);
420
+ const sites = keySites(ctx);
421
+ const devices = await readDevices(ctx, mark, kind, window, INVENTORY[kind].sections, "jamf_get_device");
422
+ return json({
423
+ id: String(group.id),
424
+ name: group.name ?? null,
425
+ smart: group.is_smart ?? null,
426
+ site: group.site?.name ?? null,
427
+ site_id: group.site?.id ?? null,
428
+ member_count: ids.length,
429
+ members: window
430
+ .map((i) => devices.get(i))
431
+ .filter((d) => Boolean(d) && inSites(sites, siteOf(kind, d)))
432
+ .map((d) => summary(kind, d)),
433
+ next: start + max_results < ids.length ? String(start + max_results) : null,
434
+ });
435
+ }));
436
+ if (allowed.has("jamf_change_group_members"))
437
+ server.registerTool("jamf_change_group_members", {
438
+ description: "Add devices to, or remove them from, a static computer or mobile device group (at most 50 in one call). Policies, configuration profiles and apps scoped to the group then reach, or leave, those devices. Smart groups cannot be changed: their members follow their criteria.",
439
+ inputSchema: z.object({
440
+ kind,
441
+ group_id: z.string().max(10).describe("A static group id, from jamf_list_groups."),
442
+ add: z.array(z.string().max(10)).max(50).default([]),
443
+ remove: z.array(z.string().max(10)).max(50).default([]),
444
+ }),
445
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
446
+ }, ({ kind, group_id, add, remove }) => audited(ctx, "jamf_change_group_members", `${kind} group ${group_id}`, async (mark, retarget) => {
447
+ const added = [...new Set(add)];
448
+ const removed = [...new Set(remove)];
449
+ if (!added.length && !removed.length)
450
+ throw new Denied("Give at least one device id to add or remove");
451
+ if (added.length + removed.length > 50)
452
+ throw new Denied("Change at most 50 devices in one call");
453
+ const both = added.filter((i) => removed.includes(i));
454
+ if (both.length)
455
+ throw new Denied(`Both added and removed: ${both.join(", ")}`);
456
+ const group = await openGroup(ctx, mark, kind, group_id);
457
+ if (group.is_smart)
458
+ throw new Denied(`Group ${group_id} is a smart group: its members follow its criteria. Only static groups can be changed.`);
459
+ await openDevices(ctx, mark, kind, [...added, ...removed]);
460
+ const g = CLASSIC_GROUP[kind];
461
+ const items = (ids) => ids.map((i) => `<${g.item}><id>${i}</id></${g.item}>`).join("");
462
+ const xml = `<${g.root}>` +
463
+ (added.length ? `<${g.item}_additions>${items(added)}</${g.item}_additions>` : "") +
464
+ (removed.length
465
+ ? `<${g.item}_deletions>${items(removed)}</${g.item}_deletions>`
466
+ : "") +
467
+ `</${g.root}>`;
468
+ await ok(ctx, mark, {
469
+ method: "PUT",
470
+ path: `${g.path}/${group_id}`,
471
+ body: xml,
472
+ headers: { "content-type": "application/xml" },
473
+ });
474
+ retarget(`${kind} group ${group_id}: added ${added.join(",") || "none"}; removed ${removed.join(",") || "none"}`);
475
+ return json({ group_id, name: group.name ?? null, added, removed });
476
+ }));
477
+ if (allowed.has("jamf_send_blank_push"))
478
+ server.registerTool("jamf_send_blank_push", {
479
+ description: "Ask computers or mobile devices (at most 50) to check in with Jamf Pro now, so they pick up pending commands, profiles and apps. Changes nothing on the device.",
480
+ inputSchema: z.object({
481
+ kind,
482
+ device_ids: z.array(z.string().max(10)).min(1).max(50),
483
+ }),
484
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
485
+ }, ({ kind, device_ids }) => audited(ctx, "jamf_send_blank_push", `${kind} ${device_ids.join(",")}`, async (mark) => {
486
+ const ids = [...new Set(device_ids)];
487
+ const devices = await openDevices(ctx, mark, kind, ids);
488
+ const byManagementId = new Map(ids.map((i) => [managementId(kind, devices.get(i)), i]));
489
+ const data = await ok(ctx, mark, {
490
+ method: "POST",
491
+ path: "/api/v2/mdm/blank-push",
492
+ body: { clientManagementIds: [...byManagementId.keys()] },
493
+ });
494
+ const failed = (data.errorUuids ?? []).map((u) => byManagementId.get(u) ?? u);
495
+ return json({ sent: ids.filter((i) => !failed.includes(i)), failed });
496
+ }));
497
+ if (allowed.has("jamf_restart_device"))
498
+ server.registerTool("jamf_restart_device", {
499
+ description: "Restart a computer or mobile device. Whoever is using it loses unsaved work. With `notify_user` (computers), the user is told and can postpone it.",
500
+ inputSchema: z.object({
501
+ kind,
502
+ device_id: id,
503
+ notify_user: z.boolean().default(true).describe("Computers only."),
504
+ }),
505
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
506
+ }, ({ kind, device_id, notify_user }) => audited(ctx, "jamf_restart_device", `${kind} ${device_id}`, async (mark) => {
507
+ const d = await openDevice(ctx, mark, kind, device_id);
508
+ return mdmCommand(ctx, mark, kind, d, {
509
+ commandType: "RESTART_DEVICE",
510
+ ...(kind === "computer" ? { notifyUser: notify_user } : {}),
511
+ });
512
+ }));
513
+ if (allowed.has("jamf_lock_device"))
514
+ server.registerTool("jamf_lock_device", {
515
+ description: "Lock a computer or mobile device, showing an optional message and phone number on its lock screen. A computer restarts and needs the 6-digit `pin` to unlock (required for computers; keep it for whoever unlocks it). A mobile device unlocks with its own passcode.",
516
+ inputSchema: z.object({
517
+ kind,
518
+ device_id: id,
519
+ pin: z.string().max(6).optional().describe("Computers only: 6 digits."),
520
+ message: z.string().max(200).optional(),
521
+ phone_number: z.string().max(40).optional(),
522
+ }),
523
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
524
+ }, ({ kind, device_id, pin, message, phone_number }) => audited(ctx, "jamf_lock_device", `${kind} ${device_id}`, async (mark) => {
525
+ if (kind === "computer" && !(pin && PIN.test(pin)))
526
+ throw new Denied("Locking a computer needs a 6-digit pin");
527
+ if (kind === "mobile" && pin)
528
+ throw new Denied("A mobile device unlocks with its own passcode: leave pin out");
529
+ const d = await openDevice(ctx, mark, kind, device_id);
530
+ return mdmCommand(ctx, mark, kind, d, {
531
+ commandType: "DEVICE_LOCK",
532
+ ...(pin ? { pin } : {}),
533
+ ...(message ? { message } : {}),
534
+ ...(phone_number ? { phoneNumber: phone_number } : {}),
535
+ });
536
+ }));
537
+ if (allowed.has("jamf_erase_device"))
538
+ server.registerTool("jamf_erase_device", {
539
+ description: "Erase a computer or mobile device: everything on it is deleted for good and it returns to factory settings. Give its exact serial number as `confirm_serial_number` (from jamf_get_device); a mismatch is refused. Some older Macs need a 6-digit `pin` to start up again.",
540
+ inputSchema: z.object({
541
+ kind,
542
+ device_id: id,
543
+ confirm_serial_number: z.string().max(40),
544
+ pin: z.string().max(6).optional().describe("Computers only: 6 digits."),
545
+ }),
546
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
547
+ }, ({ kind, device_id, confirm_serial_number, pin }) => audited(ctx, "jamf_erase_device", `${kind} ${device_id}`, async (mark, retarget) => {
548
+ if (pin !== undefined && (kind === "mobile" || !PIN.test(pin)))
549
+ throw new Denied("A pin is for computers only, and is 6 digits");
550
+ const d = await openDevice(ctx, mark, kind, device_id);
551
+ const serial = d.hardware?.serialNumber ?? "";
552
+ if (!serial || serial.toLowerCase() !== confirm_serial_number.trim().toLowerCase())
553
+ throw new Denied(`The serial number does not match ${LABEL[kind]} ${device_id}. Read it with jamf_get_device and check it is the device to erase.`);
554
+ retarget(`${kind} ${device_id} (serial ${serial})`);
555
+ return mdmCommand(ctx, mark, kind, d, {
556
+ commandType: "ERASE_DEVICE",
557
+ ...(pin ? { pin } : {}),
558
+ });
559
+ }));
560
+ if (allowed.has("jamf_list_policies"))
561
+ server.registerTool("jamf_list_policies", {
562
+ description: "List Jamf Pro policies (what runs on computers: installs, scripts, settings) by id and name. Optionally only names containing `search`. When `next` is set, call again with `cursor` set to it.",
563
+ inputSchema: z.object({
564
+ search: z.string().max(100).optional(),
565
+ max_results: z.number().int().min(1).max(100).default(50),
566
+ cursor,
567
+ }),
568
+ annotations: readOnly,
569
+ }, ({ search, max_results, cursor }) => audited(ctx, "jamf_list_policies", search ?? null, async (mark) => {
570
+ const data = await ok(ctx, mark, {
571
+ method: "GET",
572
+ path: "/JSSResource/policies",
573
+ });
574
+ const s = search?.trim().toLowerCase();
575
+ const all = (data.policies ?? []).filter((p) => !s || (p.name ?? "").toLowerCase().includes(s));
576
+ const sites = keySites(ctx);
577
+ let at = offset(cursor);
578
+ const policies = [];
579
+ let reads = 0;
580
+ for (; at < all.length && policies.length < max_results; at++) {
581
+ const p = all[at];
582
+ if (sites) {
583
+ if (reads++ >= MAX_POLICY_READS)
584
+ break;
585
+ const { inScope } = await readPolicy(ctx, mark, String(p.id), "/subset/General");
586
+ if (!inScope)
587
+ continue;
588
+ }
589
+ policies.push({ id: String(p.id), name: p.name ?? null });
590
+ }
591
+ return json({ policies, next: at < all.length ? String(at) : null });
592
+ }));
593
+ if (allowed.has("jamf_get_policy"))
594
+ server.registerTool("jamf_get_policy", {
595
+ description: "One Jamf Pro policy: whether it is on, how often and on what triggers it runs, its category and site, what it is scoped to (all computers, groups, how many computers by hand, excluded groups), its packages and scripts, and whether it is offered in Self Service.",
596
+ inputSchema: z.object({
597
+ policy_id: z.string().max(10).describe("A policy id, from jamf_list_policies."),
598
+ }),
599
+ annotations: readOnly,
600
+ }, ({ policy_id }) => audited(ctx, "jamf_get_policy", policy_id, async (mark) => {
601
+ if (!ID.test(policy_id))
602
+ throw new Denied("Give a policy id from jamf_list_policies");
603
+ const { policy, inScope } = await readPolicy(ctx, mark, policy_id);
604
+ if (!inScope)
605
+ throw new Denied(`No policy ${policy_id} that this agent may use`);
606
+ const g = policy.general ?? {};
607
+ return json({
608
+ id: policy_id,
609
+ name: g.name ?? null,
610
+ enabled: g.enabled ?? null,
611
+ frequency: g.frequency ?? null,
612
+ triggers: [
613
+ ...TRIGGERS.filter((t) => g[t] === true).map((t) => t.replace("trigger_", "")),
614
+ ...(g.trigger_other ? [g.trigger_other] : []),
615
+ ],
616
+ category: g.category?.name ?? null,
617
+ site: g.site?.name ?? null,
618
+ scope: {
619
+ all_computers: policy.scope?.all_computers ?? null,
620
+ computer_groups: (policy.scope?.computer_groups ?? []).map((x) => x.name),
621
+ computers: (policy.scope?.computers ?? []).length,
622
+ excluded_computer_groups: (policy.scope?.exclusions?.computer_groups ?? []).map((x) => x.name),
623
+ },
624
+ packages: (policy.package_configuration?.packages ?? []).map((x) => x.name),
625
+ scripts: (policy.scripts ?? []).map((x) => x.name),
626
+ self_service: policy.self_service?.use_for_self_service ?? null,
627
+ });
628
+ }));
629
+ if (allowed.has("jamf_computer_policy_logs"))
630
+ server.registerTool("jamf_computer_policy_logs", {
631
+ description: "The policy runs Jamf Pro logged on one computer, newest first: policy id and name, status (such as Completed or Failed), when, and the user signed in. When `next` is set, call again with `cursor` set to it.",
632
+ inputSchema: z.object({
633
+ computer_id: z.string().max(10).describe("A computer id, from jamf_search_devices."),
634
+ max_results: z.number().int().min(1).max(100).default(25),
635
+ cursor,
636
+ }),
637
+ annotations: readOnly,
638
+ }, ({ computer_id, max_results, cursor }) => audited(ctx, "jamf_computer_policy_logs", computer_id, async (mark) => {
639
+ await openDevice(ctx, mark, "computer", computer_id, ["GENERAL"]);
640
+ const data = await ok(ctx, mark, {
641
+ method: "GET",
642
+ path: `/JSSResource/computerhistory/id/${computer_id}/subset/PolicyLogs`,
643
+ });
644
+ const logs = [...(data.computer_history?.policy_logs ?? [])].sort((a, b) => Number(b.date_time_epoch ?? 0) - Number(a.date_time_epoch ?? 0));
645
+ const start = offset(cursor);
646
+ return json({
647
+ logs: logs.slice(start, start + max_results).map((l) => ({
648
+ policy_id: l.policy_id ?? null,
649
+ policy_name: l.policy_name ?? null,
650
+ status: l.status ?? null,
651
+ completed: l.date_time_utc ?? null,
652
+ username: l.username || null,
653
+ })),
654
+ next: start + max_results < logs.length ? String(start + max_results) : null,
655
+ });
656
+ }));
657
+ },
658
+ };
659
+ //# sourceMappingURL=tools.js.map