@arbium/enterprise-agent-connect 0.0.0-stage → 0.1.1
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/LICENSE +202 -0
- package/README.md +117 -2
- package/dist/connectors/dell-techdirect/connector.json +39 -0
- package/dist/connectors/dell-techdirect/index.js +7 -0
- package/dist/connectors/dell-techdirect/index.js.map +1 -0
- package/dist/connectors/dell-techdirect/tools.js +49 -0
- package/dist/connectors/dell-techdirect/tools.js.map +1 -0
- package/dist/connectors/dell-techdirect/vendor.js +57 -0
- package/dist/connectors/dell-techdirect/vendor.js.map +1 -0
- package/dist/connectors/entra/connector.json +85 -0
- package/dist/connectors/entra/index.js +4 -0
- package/dist/connectors/entra/index.js.map +1 -0
- package/dist/connectors/entra/tools.js +262 -0
- package/dist/connectors/entra/tools.js.map +1 -0
- package/dist/connectors/fedex/connector.json +118 -0
- package/dist/connectors/fedex/index.js +7 -0
- package/dist/connectors/fedex/index.js.map +1 -0
- package/dist/connectors/fedex/tools.js +152 -0
- package/dist/connectors/fedex/tools.js.map +1 -0
- package/dist/connectors/fedex/vendor.js +98 -0
- package/dist/connectors/fedex/vendor.js.map +1 -0
- package/dist/connectors/http/index.js +235 -0
- package/dist/connectors/http/index.js.map +1 -0
- package/dist/connectors/intune/connector.json +106 -0
- package/dist/connectors/intune/index.js +4 -0
- package/dist/connectors/intune/index.js.map +1 -0
- package/dist/connectors/intune/tools.js +416 -0
- package/dist/connectors/intune/tools.js.map +1 -0
- package/dist/connectors/jira/connector.json +129 -0
- package/dist/connectors/jira/index.js +7 -0
- package/dist/connectors/jira/index.js.map +1 -0
- package/dist/connectors/jira/jql.js +67 -0
- package/dist/connectors/jira/jql.js.map +1 -0
- package/dist/connectors/jira/tools.js +464 -0
- package/dist/connectors/jira/tools.js.map +1 -0
- package/dist/connectors/jira/vendor.js +94 -0
- package/dist/connectors/jira/vendor.js.map +1 -0
- package/dist/connectors/microsoft/client.js +434 -0
- package/dist/connectors/microsoft/client.js.map +1 -0
- package/dist/connectors/microsoft/provider.json +118 -0
- package/dist/connectors/outlook-calendar/connector.json +136 -0
- package/dist/connectors/outlook-calendar/index.js +4 -0
- package/dist/connectors/outlook-calendar/index.js.map +1 -0
- package/dist/connectors/outlook-calendar/tools.js +398 -0
- package/dist/connectors/outlook-calendar/tools.js.map +1 -0
- package/dist/connectors/outlook-mail/connector.json +154 -0
- package/dist/connectors/outlook-mail/index.js +4 -0
- package/dist/connectors/outlook-mail/index.js.map +1 -0
- package/dist/connectors/outlook-mail/tools.js +236 -0
- package/dist/connectors/outlook-mail/tools.js.map +1 -0
- package/dist/connectors/salesforce/connector.json +122 -0
- package/dist/connectors/salesforce/index.js +7 -0
- package/dist/connectors/salesforce/index.js.map +1 -0
- package/dist/connectors/salesforce/soql.js +60 -0
- package/dist/connectors/salesforce/soql.js.map +1 -0
- package/dist/connectors/salesforce/tools.js +234 -0
- package/dist/connectors/salesforce/tools.js.map +1 -0
- package/dist/connectors/salesforce/vendor.js +107 -0
- package/dist/connectors/salesforce/vendor.js.map +1 -0
- package/dist/connectors/sharepoint-files/connector.json +194 -0
- package/dist/connectors/sharepoint-files/excel.js +300 -0
- package/dist/connectors/sharepoint-files/excel.js.map +1 -0
- package/dist/connectors/sharepoint-files/index.js +12 -0
- package/dist/connectors/sharepoint-files/index.js.map +1 -0
- package/dist/connectors/sharepoint-files/tools.js +365 -0
- package/dist/connectors/sharepoint-files/tools.js.map +1 -0
- package/dist/connectors/sharepoint-files/vendor.js +167 -0
- package/dist/connectors/sharepoint-files/vendor.js.map +1 -0
- package/dist/connectors/teams/connector.json +298 -0
- package/dist/connectors/teams/index.js +5 -0
- package/dist/connectors/teams/index.js.map +1 -0
- package/dist/connectors/teams/tools.js +391 -0
- package/dist/connectors/teams/tools.js.map +1 -0
- package/dist/packages/connector-kit/src/branding.js +102 -0
- package/dist/packages/connector-kit/src/branding.js.map +1 -0
- package/dist/packages/connector-kit/src/certificate.js +84 -0
- package/dist/packages/connector-kit/src/certificate.js.map +1 -0
- package/dist/packages/connector-kit/src/common.js +107 -0
- package/dist/packages/connector-kit/src/common.js.map +1 -0
- package/dist/packages/connector-kit/src/connector.js +492 -0
- package/dist/packages/connector-kit/src/connector.js.map +1 -0
- package/dist/packages/connector-kit/src/oauth.js +79 -0
- package/dist/packages/connector-kit/src/oauth.js.map +1 -0
- package/dist/packages/connector-kit/src/types.js +20 -0
- package/dist/packages/connector-kit/src/types.js.map +1 -0
- package/dist/packages/server/migrations/0001_tables.sql +268 -0
- package/dist/packages/server/migrations/0002_organizations.sql +72 -0
- package/dist/packages/server/migrations/0003_drop_unused.sql +3 -0
- package/dist/packages/server/migrations/0004_trigger_runners.sql +15 -0
- package/dist/packages/server/migrations/0005_run_runner.sql +3 -0
- package/dist/packages/server/migrations/0006_approvals.sql +36 -0
- package/dist/packages/server/migrations/rollback/to-legacy.sql +87 -0
- package/dist/packages/server/src/api.js +684 -0
- package/dist/packages/server/src/api.js.map +1 -0
- package/dist/packages/server/src/app.js +444 -0
- package/dist/packages/server/src/app.js.map +1 -0
- package/dist/packages/server/src/builtin.js +24 -0
- package/dist/packages/server/src/builtin.js.map +1 -0
- package/dist/packages/server/src/consent.js +36 -0
- package/dist/packages/server/src/consent.js.map +1 -0
- package/dist/packages/server/src/index.js +129 -0
- package/dist/packages/server/src/index.js.map +1 -0
- package/dist/packages/server/src/migrate.js +134 -0
- package/dist/packages/server/src/migrate.js.map +1 -0
- package/dist/packages/server/src/openapi.js +101 -0
- package/dist/packages/server/src/openapi.js.map +1 -0
- package/dist/packages/server/src/secrets.js +76 -0
- package/dist/packages/server/src/secrets.js.map +1 -0
- package/dist/packages/server/src/server.js +69 -0
- package/dist/packages/server/src/server.js.map +1 -0
- package/dist/packages/server/src/service.js +908 -0
- package/dist/packages/server/src/service.js.map +1 -0
- package/dist/packages/server/src/store.js +411 -0
- package/dist/packages/server/src/store.js.map +1 -0
- package/dist/packages/server/src/teams/activity.js +187 -0
- package/dist/packages/server/src/teams/activity.js.map +1 -0
- package/dist/packages/server/src/teams/admin.js +699 -0
- package/dist/packages/server/src/teams/admin.js.map +1 -0
- package/dist/packages/server/src/teams/approval.js +118 -0
- package/dist/packages/server/src/teams/approval.js.map +1 -0
- package/dist/packages/server/src/teams/bot.js +124 -0
- package/dist/packages/server/src/teams/bot.js.map +1 -0
- package/dist/packages/server/src/teams/claude.js +165 -0
- package/dist/packages/server/src/teams/claude.js.map +1 -0
- package/dist/packages/server/src/teams/jwt.js +121 -0
- package/dist/packages/server/src/teams/jwt.js.map +1 -0
- package/dist/packages/server/src/teams/package.js +319 -0
- package/dist/packages/server/src/teams/package.js.map +1 -0
- package/dist/packages/server/src/teams/provision.js +505 -0
- package/dist/packages/server/src/teams/provision.js.map +1 -0
- package/dist/packages/server/src/teams/runner.js +143 -0
- package/dist/packages/server/src/teams/runner.js.map +1 -0
- package/dist/packages/server/src/teams/runtime.js +701 -0
- package/dist/packages/server/src/teams/runtime.js.map +1 -0
- package/dist/packages/server/src/teams/store.js +507 -0
- package/dist/packages/server/src/teams/store.js.map +1 -0
- package/package.json +42 -4
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { audited, Denied, json, listLimit, VendorFailed, } from "../../packages/connector-kit/src/common.js";
|
|
3
|
+
import { MAX_SCOPE_IDS } from "../entra/tools.js";
|
|
4
|
+
import { cursorArg, nextCursor, ok, pageQuery } from "../sharepoint-files/tools.js";
|
|
5
|
+
// Devices Microsoft Intune manages, read-only. A key's `groups` limit keeps it to devices whose
|
|
6
|
+
// primary user (managedDevice.userId) is in those groups, checked on every call, lookups by id
|
|
7
|
+
// included. Request shapes: learn.microsoft.com/graph/api/intune-devices-manageddevice-list,
|
|
8
|
+
// intune-devices-manageddevice-get, resources/intune-devices-user (a user's managedDevices),
|
|
9
|
+
// resources/intune-devices-manageddevice (v1.0 and beta: the
|
|
10
|
+
// detectedApps relationship is beta only), intune-devices-detectedapp-list and
|
|
11
|
+
// directoryobject-checkmemberobjects. intune_autopilot_status: v1.0 windowsAutopilotDeviceIdentity
|
|
12
|
+
// (list; resources/intune-enrollment-windowsautopilotdeviceidentity) and, for the profile name,
|
|
13
|
+
// the beta `deploymentProfile` relationship (intune-shared-windowsautopilotdeploymentprofile-get),
|
|
14
|
+
// both DeviceManagementServiceConfig.Read.All (read October 25, 2026).
|
|
15
|
+
const GUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16
|
+
const UPN = /^[^\s/\\?#%'"]+@[^\s/\\?#%'"]+$/;
|
|
17
|
+
const DEVICES = "/v1.0/deviceManagement/managedDevices";
|
|
18
|
+
const AUTOPILOT = "/v1.0/deviceManagement/windowsAutopilotDeviceIdentities";
|
|
19
|
+
// Letters, digits and dashes: nothing that could change a $filter or a path.
|
|
20
|
+
const SERIAL = /^[A-Za-z0-9-]{1,50}$/;
|
|
21
|
+
const NO_ID = "00000000-0000-0000-0000-000000000000";
|
|
22
|
+
const AUTOPILOT_PAGES = 5;
|
|
23
|
+
// Under a groups limit, an Autopilot device no person is tied to (no primary user, no assigned
|
|
24
|
+
// user: a spare). "minimum" answers only its serial, Autopilot state and whether Intune manages
|
|
25
|
+
// it and its compliance; "refuse" treats it as out of scope, as the other Intune tools treat a
|
|
26
|
+
// device with no user. Open question for the owner: change this one line to "refuse".
|
|
27
|
+
const UNASSIGNED_UNDER_GROUPS = "minimum";
|
|
28
|
+
export const COMPLIANCE_STATES = [
|
|
29
|
+
"unknown",
|
|
30
|
+
"compliant",
|
|
31
|
+
"noncompliant",
|
|
32
|
+
"conflict",
|
|
33
|
+
"error",
|
|
34
|
+
"inGracePeriod",
|
|
35
|
+
"configManager",
|
|
36
|
+
];
|
|
37
|
+
// ponytail: the summary reads at most this many devices; a server-side report when
|
|
38
|
+
// fleets outgrow it.
|
|
39
|
+
export const SUMMARY_MAX_DEVICES = 5000;
|
|
40
|
+
/** The key's groups, refused when Intune is not set up to check them. */
|
|
41
|
+
function keyGroups(ctx) {
|
|
42
|
+
const groups = listLimit(ctx.limits, "groups");
|
|
43
|
+
if (!groups)
|
|
44
|
+
return null;
|
|
45
|
+
if (ctx.config.people_groups !== "groups")
|
|
46
|
+
throw new Denied('This key is kept to groups, but Microsoft Intune is not set up to check groups. An administrator has to choose "Keep keys to people\'s groups" on the Microsoft Intune page.');
|
|
47
|
+
if (groups.length > MAX_SCOPE_IDS)
|
|
48
|
+
throw new Denied(`This key names more than ${MAX_SCOPE_IDS} groups. Ask an administrator to narrow it.`);
|
|
49
|
+
return groups;
|
|
50
|
+
}
|
|
51
|
+
/** Whether a user (id or sign-in name) is in any of the groups; one Graph does not find is in none. */
|
|
52
|
+
async function inGroups(ctx, mark, groups, user) {
|
|
53
|
+
const res = await ctx.call({
|
|
54
|
+
method: "POST",
|
|
55
|
+
path: `/v1.0/users/${encodeURIComponent(user)}/checkMemberObjects`,
|
|
56
|
+
body: { ids: groups },
|
|
57
|
+
});
|
|
58
|
+
mark(res.status);
|
|
59
|
+
if (res.status === 404)
|
|
60
|
+
return false;
|
|
61
|
+
if (res.status !== 200) {
|
|
62
|
+
const message = res.data?.error?.message;
|
|
63
|
+
throw new VendorFailed(res.status, `Microsoft Graph answered ${res.status}${message ? `: ${message}` : ""}`);
|
|
64
|
+
}
|
|
65
|
+
return (res.data.value ?? []).length > 0;
|
|
66
|
+
}
|
|
67
|
+
/** Whether each device's primary user is in the key's groups, one check per user. */
|
|
68
|
+
export function deviceFilter(ctx, mark) {
|
|
69
|
+
const groups = keyGroups(ctx);
|
|
70
|
+
if (!groups)
|
|
71
|
+
return async () => true;
|
|
72
|
+
const seen = new Map();
|
|
73
|
+
return (device) => {
|
|
74
|
+
const user = device.userId;
|
|
75
|
+
if (!user || !GUID.test(user))
|
|
76
|
+
return Promise.resolve(false);
|
|
77
|
+
if (!seen.has(user))
|
|
78
|
+
seen.set(user, inGroups(ctx, mark, groups, user));
|
|
79
|
+
return seen.get(user);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Everyone in the key's groups, read once: scanning a whole fleet then costs a few listings
|
|
83
|
+
* instead of a membership check per user. Null when the key has no groups. */
|
|
84
|
+
async function groupMembers(ctx, mark) {
|
|
85
|
+
const groups = keyGroups(ctx);
|
|
86
|
+
if (!groups)
|
|
87
|
+
return null;
|
|
88
|
+
const ids = new Set();
|
|
89
|
+
for (const group of groups) {
|
|
90
|
+
let cursor;
|
|
91
|
+
do {
|
|
92
|
+
const data = await ok(ctx, mark, {
|
|
93
|
+
method: "GET",
|
|
94
|
+
path: `/v1.0/groups/${encodeURIComponent(group)}/transitiveMembers/microsoft.graph.user?$select=id&${pageQuery(999, cursor)}`,
|
|
95
|
+
});
|
|
96
|
+
for (const u of data.value)
|
|
97
|
+
ids.add(u.id.toLowerCase());
|
|
98
|
+
cursor = nextCursor(data) ?? undefined;
|
|
99
|
+
} while (cursor);
|
|
100
|
+
}
|
|
101
|
+
return ids;
|
|
102
|
+
}
|
|
103
|
+
const summary = (d) => ({
|
|
104
|
+
id: d.id,
|
|
105
|
+
name: d.deviceName ?? null,
|
|
106
|
+
user: d.userPrincipalName ?? null,
|
|
107
|
+
user_name: d.userDisplayName ?? null,
|
|
108
|
+
os: d.operatingSystem ?? null,
|
|
109
|
+
os_version: d.osVersion ?? null,
|
|
110
|
+
compliance: d.complianceState ?? null,
|
|
111
|
+
last_sync: d.lastSyncDateTime ?? null,
|
|
112
|
+
encrypted: d.isEncrypted ?? null,
|
|
113
|
+
model: d.model ?? null,
|
|
114
|
+
manufacturer: d.manufacturer ?? null,
|
|
115
|
+
serial_number: d.serialNumber ?? null,
|
|
116
|
+
management_state: d.managementState ?? null,
|
|
117
|
+
owner: d.managedDeviceOwnerType ?? null,
|
|
118
|
+
});
|
|
119
|
+
const quoted = (v) => `'${v.replaceAll("'", "''")}'`;
|
|
120
|
+
async function allowedDevices(allowed, devices) {
|
|
121
|
+
const out = [];
|
|
122
|
+
for (const d of devices)
|
|
123
|
+
if (await allowed(d))
|
|
124
|
+
out.push(d);
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
/** One device, read and checked against the key's limits. */
|
|
128
|
+
async function openDevice(ctx, mark, id) {
|
|
129
|
+
if (!GUID.test(id))
|
|
130
|
+
throw new Denied("Give a device id from intune_list_devices");
|
|
131
|
+
const allowed = deviceFilter(ctx, mark);
|
|
132
|
+
const device = await ok(ctx, mark, { method: "GET", path: `${DEVICES}/${id}` });
|
|
133
|
+
if (!(await allowed(device)))
|
|
134
|
+
throw new Denied("This agent may not read that device");
|
|
135
|
+
return device;
|
|
136
|
+
}
|
|
137
|
+
const listed = (data, devices) => ({
|
|
138
|
+
devices: devices.map(summary),
|
|
139
|
+
more: Boolean(data["@odata.nextLink"]),
|
|
140
|
+
next: nextCursor(data),
|
|
141
|
+
});
|
|
142
|
+
/** A Graph read where 404 means "nothing there": null. */
|
|
143
|
+
async function okOrNone(ctx, mark, req) {
|
|
144
|
+
let notFound = false;
|
|
145
|
+
const data = await ok(ctx, (status) => {
|
|
146
|
+
mark(status);
|
|
147
|
+
notFound = status === 404;
|
|
148
|
+
}, req).catch((error) => {
|
|
149
|
+
if (notFound)
|
|
150
|
+
return null;
|
|
151
|
+
throw error;
|
|
152
|
+
});
|
|
153
|
+
return data;
|
|
154
|
+
}
|
|
155
|
+
/** Where Graph says a listing continues, as a path for the next call: only another page of
|
|
156
|
+
* the same listing on Graph (`@odata.nextLink` is used as given, whatever its paging
|
|
157
|
+
* parameter), never another host or resource. */
|
|
158
|
+
function nextPath(link, path) {
|
|
159
|
+
const url = URL.parse(link);
|
|
160
|
+
if (url?.origin !== "https://graph.microsoft.com" ||
|
|
161
|
+
url.pathname.toLowerCase() !== path.toLowerCase())
|
|
162
|
+
throw new Error(`Graph gave a next page outside ${path}: ${link.slice(0, 200)}`);
|
|
163
|
+
return `${url.pathname}${url.search}`;
|
|
164
|
+
}
|
|
165
|
+
/** The Autopilot identity with exactly this serial number (any case), or null. The filter is
|
|
166
|
+
* `contains` and the match is made here: Microsoft's pages for the list do not document
|
|
167
|
+
* $filter, and `contains` on serialNumber is what Intune tooling is seen to use. */
|
|
168
|
+
async function autopilotIdentity(ctx, mark, serial) {
|
|
169
|
+
const filter = encodeURIComponent(`contains(serialNumber,${quoted(serial)})`);
|
|
170
|
+
let path = `${AUTOPILOT}?$top=50&$filter=${filter}`;
|
|
171
|
+
for (let page = 0; page < AUTOPILOT_PAGES; page++) {
|
|
172
|
+
const data = await ok(ctx, mark, { method: "GET", path });
|
|
173
|
+
const hit = data.value.find((i) => String(i.serialNumber ?? "").toLowerCase() === serial.toLowerCase());
|
|
174
|
+
if (hit)
|
|
175
|
+
return hit;
|
|
176
|
+
const link = data["@odata.nextLink"];
|
|
177
|
+
if (!link)
|
|
178
|
+
return null;
|
|
179
|
+
path = nextPath(link, AUTOPILOT);
|
|
180
|
+
}
|
|
181
|
+
throw new Denied("Too many Autopilot devices match that serial number. Give the full serial number.");
|
|
182
|
+
}
|
|
183
|
+
export const intuneTools = {
|
|
184
|
+
names: [
|
|
185
|
+
"intune_list_devices",
|
|
186
|
+
"intune_get_device",
|
|
187
|
+
"intune_device_apps",
|
|
188
|
+
"intune_person_devices",
|
|
189
|
+
"intune_compliance_summary",
|
|
190
|
+
"intune_autopilot_status",
|
|
191
|
+
],
|
|
192
|
+
register(server, ctx, allowed) {
|
|
193
|
+
const readOnly = { readOnlyHint: true, openWorldHint: true };
|
|
194
|
+
const deviceId = z.string().max(36).describe("A device id, from intune_list_devices.");
|
|
195
|
+
if (allowed.has("intune_list_devices"))
|
|
196
|
+
server.registerTool("intune_list_devices", {
|
|
197
|
+
description: "List managed devices: name, user, system and version, compliance, last check-in, encryption, model and serial number. Optionally only names containing `search`, or one compliance state. When `more` is true, call again with `cursor` set to `next`.",
|
|
198
|
+
inputSchema: z.object({
|
|
199
|
+
search: z.string().max(100).optional().describe("Part of the device name."),
|
|
200
|
+
compliance: z.enum(COMPLIANCE_STATES).optional(),
|
|
201
|
+
max_results: z.number().int().min(1).max(50).default(25),
|
|
202
|
+
cursor: cursorArg,
|
|
203
|
+
}),
|
|
204
|
+
annotations: readOnly,
|
|
205
|
+
}, ({ search, compliance, max_results, cursor }) => audited(ctx, "intune_list_devices", [search, compliance].filter(Boolean).join(" ") || null, async (mark) => {
|
|
206
|
+
const allowed = deviceFilter(ctx, mark);
|
|
207
|
+
const filters = [
|
|
208
|
+
...(search?.trim() ? [`contains(deviceName,${quoted(search.trim())})`] : []),
|
|
209
|
+
...(compliance ? [`complianceState eq ${quoted(compliance)}`] : []),
|
|
210
|
+
];
|
|
211
|
+
const filter = filters.length
|
|
212
|
+
? `&$filter=${encodeURIComponent(filters.join(" and "))}`
|
|
213
|
+
: "";
|
|
214
|
+
const data = await ok(ctx, mark, {
|
|
215
|
+
method: "GET",
|
|
216
|
+
path: `${DEVICES}?${pageQuery(max_results, cursor)}${filter}`,
|
|
217
|
+
});
|
|
218
|
+
return json(listed(data, await allowedDevices(allowed, data.value)));
|
|
219
|
+
}));
|
|
220
|
+
if (allowed.has("intune_get_device"))
|
|
221
|
+
server.registerTool("intune_get_device", {
|
|
222
|
+
description: "One device's details: everything intune_list_devices shows, plus enrollment date, compliance grace period, jailbreak state and how it was enrolled.",
|
|
223
|
+
inputSchema: z.object({ device_id: deviceId }),
|
|
224
|
+
annotations: readOnly,
|
|
225
|
+
}, ({ device_id }) => audited(ctx, "intune_get_device", device_id, async (mark) => {
|
|
226
|
+
const d = await openDevice(ctx, mark, device_id);
|
|
227
|
+
return json({
|
|
228
|
+
...summary(d),
|
|
229
|
+
enrolled: d.enrolledDateTime ?? null,
|
|
230
|
+
compliance_grace_period_ends: d.complianceGracePeriodExpirationDateTime ?? null,
|
|
231
|
+
jailbroken: d.jailBroken ?? null,
|
|
232
|
+
enrollment_type: d.deviceEnrollmentType ?? null,
|
|
233
|
+
entra_device_id: d.azureADDeviceId ?? null,
|
|
234
|
+
});
|
|
235
|
+
}));
|
|
236
|
+
if (allowed.has("intune_device_apps"))
|
|
237
|
+
server.registerTool("intune_device_apps", {
|
|
238
|
+
description: "The apps Intune found on a device, with version and publisher. When `more` is true, call again with `cursor` set to `next`.",
|
|
239
|
+
inputSchema: z.object({
|
|
240
|
+
device_id: deviceId,
|
|
241
|
+
max_results: z.number().int().min(1).max(200).default(100),
|
|
242
|
+
cursor: cursorArg,
|
|
243
|
+
}),
|
|
244
|
+
annotations: readOnly,
|
|
245
|
+
}, ({ device_id, max_results, cursor }) => audited(ctx, "intune_device_apps", device_id, async (mark) => {
|
|
246
|
+
await openDevice(ctx, mark, device_id);
|
|
247
|
+
// Only Graph beta has a device's detectedApps.
|
|
248
|
+
const data = await ok(ctx, mark, {
|
|
249
|
+
method: "GET",
|
|
250
|
+
path: `/beta/deviceManagement/managedDevices/${device_id}/detectedApps?${pageQuery(max_results, cursor)}`,
|
|
251
|
+
});
|
|
252
|
+
return json({
|
|
253
|
+
apps: data.value.map((a) => ({
|
|
254
|
+
name: a.displayName ?? null,
|
|
255
|
+
version: a.version ?? null,
|
|
256
|
+
publisher: a.publisher ?? null,
|
|
257
|
+
platform: a.platform ?? null,
|
|
258
|
+
})),
|
|
259
|
+
more: Boolean(data["@odata.nextLink"]),
|
|
260
|
+
next: nextCursor(data),
|
|
261
|
+
});
|
|
262
|
+
}));
|
|
263
|
+
if (allowed.has("intune_person_devices"))
|
|
264
|
+
server.registerTool("intune_person_devices", {
|
|
265
|
+
description: "The managed devices of one person, by their sign-in name or user id. When `more` is true, call again with `cursor` set to `next`.",
|
|
266
|
+
inputSchema: z.object({
|
|
267
|
+
person: z
|
|
268
|
+
.string()
|
|
269
|
+
.max(320)
|
|
270
|
+
.describe("A sign-in name such as ada@contoso.com, or a user id."),
|
|
271
|
+
max_results: z.number().int().min(1).max(50).default(25),
|
|
272
|
+
cursor: cursorArg,
|
|
273
|
+
}),
|
|
274
|
+
annotations: readOnly,
|
|
275
|
+
}, ({ person, max_results, cursor }) => audited(ctx, "intune_person_devices", person, async (mark) => {
|
|
276
|
+
const p = person.trim();
|
|
277
|
+
if (!GUID.test(p) && !UPN.test(p))
|
|
278
|
+
throw new Denied("Give a sign-in name such as ada@contoso.com, or a user id");
|
|
279
|
+
// Same refusal for a person outside the groups and one who does not exist, as
|
|
280
|
+
// Entra gives, so a kept key cannot learn which names exist.
|
|
281
|
+
const groups = keyGroups(ctx);
|
|
282
|
+
if (groups && !(await inGroups(ctx, mark, groups, p)))
|
|
283
|
+
throw new Denied("This agent may not look up that person");
|
|
284
|
+
// The person's own devices, so the check above is the scope check: this listing
|
|
285
|
+
// leaves userId and userPrincipalName blank (seen live), which deviceFilter would
|
|
286
|
+
// read as a device with no user.
|
|
287
|
+
const data = await ok(ctx, mark, {
|
|
288
|
+
method: "GET",
|
|
289
|
+
path: `/v1.0/users/${encodeURIComponent(p)}/managedDevices?${pageQuery(max_results, cursor)}`,
|
|
290
|
+
});
|
|
291
|
+
return json(listed(data, data.value));
|
|
292
|
+
}));
|
|
293
|
+
if (allowed.has("intune_compliance_summary"))
|
|
294
|
+
server.registerTool("intune_compliance_summary", {
|
|
295
|
+
description: `How many of the devices this agent may see are in each compliance state, by operating system. Reads at most ${SUMMARY_MAX_DEVICES} devices; \`complete\` says whether that was all of them. List the devices of one state with intune_list_devices.`,
|
|
296
|
+
inputSchema: z.object({}),
|
|
297
|
+
annotations: readOnly,
|
|
298
|
+
}, () => audited(ctx, "intune_compliance_summary", null, async (mark) => {
|
|
299
|
+
const members = await groupMembers(ctx, mark);
|
|
300
|
+
const counts = {};
|
|
301
|
+
let counted = 0;
|
|
302
|
+
let scanned = 0;
|
|
303
|
+
let cursor;
|
|
304
|
+
let complete = true;
|
|
305
|
+
do {
|
|
306
|
+
const data = await ok(ctx, mark, {
|
|
307
|
+
method: "GET",
|
|
308
|
+
path: `${DEVICES}?$select=id,userId,operatingSystem,complianceState&${pageQuery(1000, cursor)}`,
|
|
309
|
+
});
|
|
310
|
+
scanned += data.value.length;
|
|
311
|
+
for (const d of data.value) {
|
|
312
|
+
if (members && !(d.userId && members.has(d.userId.toLowerCase())))
|
|
313
|
+
continue;
|
|
314
|
+
const os = String(d.operatingSystem ?? "unknown");
|
|
315
|
+
const state = String(d.complianceState ?? "unknown");
|
|
316
|
+
counts[os] ??= {};
|
|
317
|
+
counts[os][state] = (counts[os][state] ?? 0) + 1;
|
|
318
|
+
counted++;
|
|
319
|
+
}
|
|
320
|
+
cursor = nextCursor(data) ?? undefined;
|
|
321
|
+
if (cursor && scanned >= SUMMARY_MAX_DEVICES) {
|
|
322
|
+
complete = false;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
} while (cursor);
|
|
326
|
+
return json({ devices_counted: counted, complete, by_operating_system: counts });
|
|
327
|
+
}));
|
|
328
|
+
if (allowed.has("intune_autopilot_status"))
|
|
329
|
+
server.registerTool("intune_autopilot_status", {
|
|
330
|
+
description: "Where a Windows Autopilot device stands, by its exact serial number: its enrollment state (unknown, enrolled, pendingReset, failed, notContacted), assigned profile, group tag, last contact with Intune and, once Intune manages it, that device's id, compliance and primary user. Unknown to Autopilot: {serial_number, found: false}. When this key is kept to groups and no person is tied to the device, only {serial_number, found, deployment_state, managed, compliance, limited} is answered.",
|
|
331
|
+
inputSchema: z.object({
|
|
332
|
+
serial_number: z
|
|
333
|
+
.string()
|
|
334
|
+
.max(50)
|
|
335
|
+
.describe("The device's exact serial number: letters, digits and dashes only (one with spaces, dots or underscores is refused)."),
|
|
336
|
+
}),
|
|
337
|
+
annotations: readOnly,
|
|
338
|
+
}, ({ serial_number }) => audited(ctx, "intune_autopilot_status", serial_number, async (mark) => {
|
|
339
|
+
const serial = serial_number.trim();
|
|
340
|
+
if (!SERIAL.test(serial))
|
|
341
|
+
throw new Denied("Give the device's exact serial number: letters, digits and dashes only, up to 50. Serial numbers with spaces, dots or underscores (some virtual machines have them) are not accepted by this tool.");
|
|
342
|
+
const groups = keyGroups(ctx);
|
|
343
|
+
const identity = await autopilotIdentity(ctx, mark, serial);
|
|
344
|
+
if (!identity)
|
|
345
|
+
return json({ serial_number: serial, found: false });
|
|
346
|
+
const managedId = String(identity.managedDeviceId ?? "");
|
|
347
|
+
// The identity can outlive the managed device it pointed to (404). This read is the
|
|
348
|
+
// always-on `read` capability's permission, so a 403 carries that capability's hint.
|
|
349
|
+
const managed = GUID.test(managedId) && managedId !== NO_ID
|
|
350
|
+
? await okOrNone(ctx, mark, {
|
|
351
|
+
method: "GET",
|
|
352
|
+
path: `${DEVICES}/${managedId}`,
|
|
353
|
+
}).catch((error) => {
|
|
354
|
+
if (error instanceof VendorFailed)
|
|
355
|
+
throw new VendorFailed(error.status, error.message, "intune_get_device");
|
|
356
|
+
throw error;
|
|
357
|
+
})
|
|
358
|
+
: null;
|
|
359
|
+
// Under a groups limit every person tied to the device must be in the groups: the
|
|
360
|
+
// managed device's primary user and a user assigned on the Autopilot record.
|
|
361
|
+
if (groups) {
|
|
362
|
+
const inScope = deviceFilter(ctx, mark);
|
|
363
|
+
if (managed?.userId && !(await inScope(managed)))
|
|
364
|
+
throw new Denied("This agent may not read that device");
|
|
365
|
+
const assigned = String(identity.userPrincipalName ?? "").trim();
|
|
366
|
+
if (assigned &&
|
|
367
|
+
(!UPN.test(assigned) || !(await inGroups(ctx, mark, groups, assigned))))
|
|
368
|
+
throw new Denied("This agent may not read that device");
|
|
369
|
+
// Nobody tied to it (a spare): see UNASSIGNED_UNDER_GROUPS.
|
|
370
|
+
if (!managed?.userId && !assigned) {
|
|
371
|
+
if (UNASSIGNED_UNDER_GROUPS === "refuse")
|
|
372
|
+
throw new Denied("This agent may not read that device");
|
|
373
|
+
return json({
|
|
374
|
+
serial_number: String(identity.serialNumber ?? serial),
|
|
375
|
+
found: true,
|
|
376
|
+
deployment_state: identity.enrollmentState ?? null,
|
|
377
|
+
managed: Boolean(managed),
|
|
378
|
+
compliance: managed ? (managed.complianceState ?? null) : null,
|
|
379
|
+
limited: "No person is tied to this device and this key is kept to groups: only its Autopilot state and compliance are answered.",
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
// Only Graph beta has the assigned profile; a device with none is expected to answer
|
|
384
|
+
// 404. Any other failure leaves the profile out, with why, not the v1.0 answer.
|
|
385
|
+
let profile = null;
|
|
386
|
+
let profileError = null;
|
|
387
|
+
if (GUID.test(String(identity.id)))
|
|
388
|
+
profile = await okOrNone(ctx, mark, {
|
|
389
|
+
method: "GET",
|
|
390
|
+
path: `/beta/deviceManagement/windowsAutopilotDeviceIdentities/${identity.id}/deploymentProfile`,
|
|
391
|
+
}).catch((error) => {
|
|
392
|
+
if (!(error instanceof VendorFailed))
|
|
393
|
+
throw error;
|
|
394
|
+
profileError = `Microsoft Graph (beta) answered ${error.status} for the profile`;
|
|
395
|
+
return null;
|
|
396
|
+
});
|
|
397
|
+
return json({
|
|
398
|
+
serial_number: String(identity.serialNumber ?? serial),
|
|
399
|
+
found: true,
|
|
400
|
+
deployment_state: identity.enrollmentState ?? null,
|
|
401
|
+
enrollment_profile: profile?.displayName ?? null,
|
|
402
|
+
...(profileError ? { enrollment_profile_error: profileError } : {}),
|
|
403
|
+
group_tag: identity.groupTag || null,
|
|
404
|
+
last_contact: identity.lastContactedDateTime ?? null,
|
|
405
|
+
managed_device: managed
|
|
406
|
+
? {
|
|
407
|
+
id: managed.id,
|
|
408
|
+
compliance: managed.complianceState ?? null,
|
|
409
|
+
primary_user: managed.userId ? (managed.userPrincipalName ?? null) : null,
|
|
410
|
+
}
|
|
411
|
+
: null,
|
|
412
|
+
});
|
|
413
|
+
}));
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../connectors/intune/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "jira",
|
|
3
|
+
"name": "Jira",
|
|
4
|
+
"description": "Let agents search, read and work on issues in the Jira projects you choose.",
|
|
5
|
+
"logo": "/brand/jira.svg",
|
|
6
|
+
"identity": "An Atlassian service account named \"{brand.microsoft.appName}\". It uses no Jira seat.",
|
|
7
|
+
"accountKey": "cloudId",
|
|
8
|
+
"connectionKind": "guided",
|
|
9
|
+
"setup": [
|
|
10
|
+
{
|
|
11
|
+
"title": "Create a service account with Jira access",
|
|
12
|
+
"body": "In Atlassian Administration, create a service account named \"{brand.microsoft.appName}\". When it asks you to select an app role, set Jira to User and leave every other app, including Jira Administration, on None. Without a Jira role the account cannot use Jira at all.",
|
|
13
|
+
"click": [
|
|
14
|
+
"Atlassian Administration > Directory > Service accounts > Create service account",
|
|
15
|
+
"Name: {brand.microsoft.appName}",
|
|
16
|
+
"Select app role: Jira > User (every other app, including Jira Administration: None) > Create"
|
|
17
|
+
],
|
|
18
|
+
"link": {
|
|
19
|
+
"label": "Open Atlassian Administration",
|
|
20
|
+
"href": "https://admin.atlassian.com"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"title": "Add it to the projects agents should work in",
|
|
25
|
+
"body": "How you do this depends on the project. In a team-managed project (the usual kind on the Free plan), add the service account as a Member: Members can browse, comment on, move and create issues, and Viewers can only read. In a company-managed project on Standard or Premium, give it a project role whose permissions include the ones below. Without Browse projects the projects are listed but their issues stay hidden. You can narrow each agent to specific projects here later.",
|
|
26
|
+
"click": [
|
|
27
|
+
"Team-managed project: the project > Project settings > Access > Add people > {brand.microsoft.appName} > Role: Member > Add",
|
|
28
|
+
"Company-managed project: the project > Project settings > People > Add people > {brand.microsoft.appName} > a role with the permissions below"
|
|
29
|
+
],
|
|
30
|
+
"permissions": true
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"title": "Create OAuth 2.0 credentials",
|
|
34
|
+
"body": "On the service account, select Create credentials, then OAuth 2.0, and select exactly these scopes. Copy the client ID and secret: Atlassian shows the secret only once.",
|
|
35
|
+
"copy": ["{scopes}"]
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"afterConnect": [],
|
|
39
|
+
"fields": [
|
|
40
|
+
{
|
|
41
|
+
"id": "site",
|
|
42
|
+
"label": "Jira site",
|
|
43
|
+
"help": "The part before .atlassian.net in your Jira address.",
|
|
44
|
+
"placeholder": "your-company",
|
|
45
|
+
"secret": false
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "client_id",
|
|
49
|
+
"label": "Client ID",
|
|
50
|
+
"secret": false
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "client_secret",
|
|
54
|
+
"label": "Client secret",
|
|
55
|
+
"secret": true
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"capabilities": [
|
|
59
|
+
{
|
|
60
|
+
"id": "read",
|
|
61
|
+
"label": "Read issues",
|
|
62
|
+
"description": "Search and read issues and list projects.",
|
|
63
|
+
"required": true,
|
|
64
|
+
"scopes": ["read:jira-work"],
|
|
65
|
+
"permissions": ["Browse projects"],
|
|
66
|
+
"tools": ["jira_search_issues", "jira_get_issue", "jira_list_projects"]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "comment",
|
|
70
|
+
"label": "Comment",
|
|
71
|
+
"description": "Add comments to issues. In a service project a comment is public (the customer is emailed) unless the agent marks it as an internal note.",
|
|
72
|
+
"scopes": ["write:jira-work"],
|
|
73
|
+
"permissions": ["Add comments"],
|
|
74
|
+
"tools": ["jira_add_comment"]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "transition",
|
|
78
|
+
"label": "Move issues",
|
|
79
|
+
"description": "Move issues to another status, such as In Progress or Done.",
|
|
80
|
+
"scopes": ["write:jira-work"],
|
|
81
|
+
"permissions": ["Transition issues"],
|
|
82
|
+
"tools": ["jira_list_transitions", "jira_transition_issue"]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "create",
|
|
86
|
+
"label": "Create issues",
|
|
87
|
+
"description": "Create new issues in the allowed projects.",
|
|
88
|
+
"scopes": ["write:jira-work"],
|
|
89
|
+
"permissions": ["Create issues"],
|
|
90
|
+
"tools": ["jira_create_issue"]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "delete",
|
|
94
|
+
"label": "Delete issues",
|
|
95
|
+
"description": "Delete issues for good. Jira cannot bring a deleted issue back.",
|
|
96
|
+
"destructive": true,
|
|
97
|
+
"scopes": ["write:jira-work"],
|
|
98
|
+
"permissions": ["Delete issues"],
|
|
99
|
+
"tools": ["jira_delete_issue"]
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"limits": [
|
|
103
|
+
{
|
|
104
|
+
"id": "projects",
|
|
105
|
+
"label": "Projects",
|
|
106
|
+
"help": "Leave empty to allow every project the service account can see.",
|
|
107
|
+
"kind": "list",
|
|
108
|
+
"required": false,
|
|
109
|
+
"pattern": "^[A-Z][A-Z0-9_]*$"
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"verify": {
|
|
113
|
+
"method": "GET",
|
|
114
|
+
"path": "/rest/api/3/serverInfo",
|
|
115
|
+
"label": "Connected Jira site",
|
|
116
|
+
"show": "baseUrl"
|
|
117
|
+
},
|
|
118
|
+
"discover": {
|
|
119
|
+
"method": "POST",
|
|
120
|
+
"path": "/rest/api/3/permissions/project",
|
|
121
|
+
"body": {
|
|
122
|
+
"permissions": ["BROWSE_PROJECTS"]
|
|
123
|
+
},
|
|
124
|
+
"label": "Projects agents can see",
|
|
125
|
+
"show": "projects[].key",
|
|
126
|
+
"ifEmpty": "The service account cannot browse any project yet, so agents will find no issues. Give it a role with Browse projects in each project agents need, then check again."
|
|
127
|
+
},
|
|
128
|
+
"revoke": "In Atlassian Administration, open Directory, then Service accounts, then {brand.microsoft.appName}, and revoke its credentials."
|
|
129
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../connectors/jira/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,eAAe;IACb,EAAE,EAAE,MAAM;IACV,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;CACnC,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Confines an agent's JQL to the projects its grant allows. The agent's clause is kept
|
|
2
|
+
// whole inside parentheses, so it has to be lexically closed on its own: balanced
|
|
3
|
+
// parentheses outside strings, no unterminated string. Anything else could close our
|
|
4
|
+
// parenthesis early and OR its way out of the project filter, so it is refused.
|
|
5
|
+
export class JqlRejected extends Error {
|
|
6
|
+
}
|
|
7
|
+
const PROJECT_KEY = /^[A-Z][A-Z0-9_]{0,254}$/;
|
|
8
|
+
// After strings are blanked to "": fields (cf[10010], "Story Points") with optional direction.
|
|
9
|
+
const SORT_FIELD = String.raw `(?:""|[A-Za-z_][\w.]*(?:\[\d+\])?)(?:\s+(?:asc|desc))?`;
|
|
10
|
+
const ORDER_BY = new RegExp(String.raw `^order\s+by\s+${SORT_FIELD}(?:\s*,\s*${SORT_FIELD})*$`, "i");
|
|
11
|
+
export function isProjectKey(key) {
|
|
12
|
+
return PROJECT_KEY.test(key);
|
|
13
|
+
}
|
|
14
|
+
// Splits at a top-level ORDER BY (outside strings and parentheses).
|
|
15
|
+
function splitOrderBy(jql) {
|
|
16
|
+
let depth = 0;
|
|
17
|
+
let quote = null;
|
|
18
|
+
let orderAt = -1;
|
|
19
|
+
for (let i = 0; i < jql.length; i++) {
|
|
20
|
+
const c = jql[i];
|
|
21
|
+
if (quote) {
|
|
22
|
+
if (c === "\\")
|
|
23
|
+
i++;
|
|
24
|
+
else if (c === quote)
|
|
25
|
+
quote = null;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (c === '"' || c === "'")
|
|
29
|
+
quote = c;
|
|
30
|
+
else if (c === "(")
|
|
31
|
+
depth++;
|
|
32
|
+
else if (c === ")") {
|
|
33
|
+
depth--;
|
|
34
|
+
if (depth < 0)
|
|
35
|
+
throw new JqlRejected("Unbalanced parentheses in the query");
|
|
36
|
+
}
|
|
37
|
+
else if (orderAt < 0 &&
|
|
38
|
+
depth === 0 &&
|
|
39
|
+
/^order\s+by\b/i.test(jql.slice(i)) &&
|
|
40
|
+
(i === 0 || /[\s)]/.test(jql[i - 1]))) {
|
|
41
|
+
orderAt = i;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (quote)
|
|
45
|
+
throw new JqlRejected("Unterminated string in the query");
|
|
46
|
+
if (depth !== 0)
|
|
47
|
+
throw new JqlRejected("Unbalanced parentheses in the query");
|
|
48
|
+
if (orderAt < 0)
|
|
49
|
+
return { where: jql.trim(), orderBy: null };
|
|
50
|
+
return { where: jql.slice(0, orderAt).trim(), orderBy: jql.slice(orderAt).trim() };
|
|
51
|
+
}
|
|
52
|
+
export function scopeJql(jql, projects) {
|
|
53
|
+
const { where, orderBy } = splitOrderBy(jql);
|
|
54
|
+
if (orderBy && !ORDER_BY.test(orderBy.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/g, '""')))
|
|
55
|
+
throw new JqlRejected("ORDER BY must be the last part of the query and list only fields");
|
|
56
|
+
if (projects === null)
|
|
57
|
+
return jql.trim();
|
|
58
|
+
if (projects.length === 0)
|
|
59
|
+
throw new JqlRejected("No projects are allowed");
|
|
60
|
+
for (const p of projects)
|
|
61
|
+
if (!isProjectKey(p))
|
|
62
|
+
throw new Error(`Invalid project key ${p}`);
|
|
63
|
+
const filter = `project in (${projects.map((p) => `"${p}"`).join(", ")})`;
|
|
64
|
+
const scoped = where ? `${filter} AND (${where})` : filter;
|
|
65
|
+
return orderBy ? `${scoped} ${orderBy}` : scoped;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=jql.js.map
|