@arbium/enterprise-agent-connect 0.1.2-dev.d2aa5ca → 0.2.1-dev.fd7a648
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 +15 -5
- package/dist/connectors/dynamics-365/connector.json +16 -1
- package/dist/connectors/dynamics-365/index.d.ts +1 -4
- package/dist/connectors/dynamics-365/index.js.map +1 -1
- package/dist/connectors/entra/index.d.ts +1 -1
- package/dist/connectors/gong/vendor.d.ts +1 -1
- package/dist/connectors/gong/vendor.js +24 -32
- package/dist/connectors/gong/vendor.js.map +1 -1
- package/dist/connectors/http/index.d.ts +2 -6
- package/dist/connectors/http/index.js +4 -1
- package/dist/connectors/http/index.js.map +1 -1
- package/dist/connectors/http/openapi.d.ts +62 -0
- package/dist/connectors/http/openapi.js +342 -0
- package/dist/connectors/http/openapi.js.map +1 -0
- package/dist/connectors/intune/index.d.ts +1 -1
- package/dist/connectors/jamf/vendor.js +5 -1
- package/dist/connectors/jamf/vendor.js.map +1 -1
- package/dist/connectors/jira/connector.json +3 -3
- package/dist/connectors/jira/vendor.d.ts +1 -1
- package/dist/connectors/jira/vendor.js +8 -2
- package/dist/connectors/jira/vendor.js.map +1 -1
- package/dist/connectors/microsoft/client.d.ts +11 -3
- package/dist/connectors/microsoft/client.js +62 -8
- package/dist/connectors/microsoft/client.js.map +1 -1
- package/dist/connectors/netsuite/connector.json +4 -2
- package/dist/connectors/netsuite/vendor.d.ts +1 -1
- package/dist/connectors/netsuite/vendor.js +5 -6
- package/dist/connectors/netsuite/vendor.js.map +1 -1
- package/dist/connectors/odata/index.d.ts +1 -4
- package/dist/connectors/odata/index.js.map +1 -1
- package/dist/connectors/okta/connector.json +3 -2
- package/dist/connectors/okta/vendor.d.ts +1 -1
- package/dist/connectors/okta/vendor.js +7 -6
- package/dist/connectors/okta/vendor.js.map +1 -1
- package/dist/connectors/outlook-calendar/index.d.ts +1 -1
- package/dist/connectors/outlook-mail/index.d.ts +1 -1
- package/dist/connectors/salesforce/vendor.d.ts +1 -1
- package/dist/connectors/salesforce/vendor.js +9 -3
- package/dist/connectors/salesforce/vendor.js.map +1 -1
- package/dist/connectors/sap-s4hana/connector.json +1 -0
- package/dist/connectors/servicenow/vendor.d.ts +0 -1
- package/dist/connectors/servicenow/vendor.js +7 -10
- package/dist/connectors/servicenow/vendor.js.map +1 -1
- package/dist/connectors/sharepoint-files/connector.json +34 -3
- package/dist/connectors/sharepoint-files/documents.d.ts +7 -0
- package/dist/connectors/sharepoint-files/documents.js +212 -0
- package/dist/connectors/sharepoint-files/documents.js.map +1 -0
- package/dist/connectors/sharepoint-files/docx-worker.d.ts +7 -0
- package/dist/connectors/sharepoint-files/docx-worker.js +41 -0
- package/dist/connectors/sharepoint-files/docx-worker.js.map +1 -0
- package/dist/connectors/sharepoint-files/docx.d.ts +96 -0
- package/dist/connectors/sharepoint-files/docx.js +823 -0
- package/dist/connectors/sharepoint-files/docx.js.map +1 -0
- package/dist/connectors/sharepoint-files/index.d.ts +1 -4
- package/dist/connectors/sharepoint-files/index.js +2 -1
- package/dist/connectors/sharepoint-files/index.js.map +1 -1
- package/dist/connectors/sharepoint-files/ooxml.d.ts +19 -0
- package/dist/connectors/sharepoint-files/ooxml.js +179 -0
- package/dist/connectors/sharepoint-files/ooxml.js.map +1 -0
- package/dist/connectors/sharepoint-files/paging.d.ts +15 -0
- package/dist/connectors/sharepoint-files/paging.js +87 -0
- package/dist/connectors/sharepoint-files/paging.js.map +1 -0
- package/dist/connectors/sharepoint-files/parse-host.mjs +78 -0
- package/dist/connectors/sharepoint-files/pdf-worker.mjs +108 -0
- package/dist/connectors/sharepoint-files/pdf.d.ts +16 -0
- package/dist/connectors/sharepoint-files/pdf.js +19 -0
- package/dist/connectors/sharepoint-files/pdf.js.map +1 -0
- package/dist/connectors/sharepoint-files/sandbox.d.ts +19 -0
- package/dist/connectors/sharepoint-files/sandbox.js +176 -0
- package/dist/connectors/sharepoint-files/sandbox.js.map +1 -0
- package/dist/connectors/sharepoint-files/tools.d.ts +6 -0
- package/dist/connectors/sharepoint-files/tools.js +79 -4
- package/dist/connectors/sharepoint-files/tools.js.map +1 -1
- package/dist/connectors/slack/vendor.d.ts +1 -1
- package/dist/connectors/slack/vendor.js +32 -38
- package/dist/connectors/slack/vendor.js.map +1 -1
- package/dist/connectors/soap/index.d.ts +1 -4
- package/dist/connectors/sql/index.d.ts +1 -4
- package/dist/connectors/sql/index.js +2 -0
- package/dist/connectors/sql/index.js.map +1 -1
- package/dist/connectors/teams/index.d.ts +1 -1
- package/dist/connectors/teams/tools.d.ts +6 -1
- package/dist/connectors/teams/tools.js +3 -2
- package/dist/connectors/teams/tools.js.map +1 -1
- package/dist/connectors/workday/connector.json +6 -4
- package/dist/connectors/workday/vendor.d.ts +1 -1
- package/dist/connectors/workday/vendor.js +8 -4
- package/dist/connectors/workday/vendor.js.map +1 -1
- package/dist/packages/connector-kit/src/common.d.ts +4 -5
- package/dist/packages/connector-kit/src/common.js +4 -1
- package/dist/packages/connector-kit/src/common.js.map +1 -1
- package/dist/packages/connector-kit/src/connector.d.ts +37 -27
- package/dist/packages/connector-kit/src/connector.js +50 -11
- package/dist/packages/connector-kit/src/connector.js.map +1 -1
- package/dist/packages/connector-kit/src/fetch.d.ts +6 -0
- package/dist/packages/connector-kit/src/fetch.js +44 -0
- package/dist/packages/connector-kit/src/fetch.js.map +1 -0
- package/dist/packages/connector-kit/src/index.d.ts +3 -3
- package/dist/packages/connector-kit/src/index.js +1 -0
- package/dist/packages/connector-kit/src/index.js.map +1 -1
- package/dist/packages/connector-kit/src/oauth.d.ts +1 -1
- package/dist/packages/connector-kit/src/oauth.js +4 -2
- package/dist/packages/connector-kit/src/oauth.js.map +1 -1
- package/dist/packages/connector-kit/src/types.d.ts +4 -1
- package/dist/packages/connector-kit/src/types.js.map +1 -1
- package/dist/packages/server/migrations/0008_internal_apps.sql +16 -0
- package/dist/packages/server/migrations/rollback/to-legacy.sql +2 -0
- package/dist/packages/server/src/api.d.ts +2417 -253
- package/dist/packages/server/src/api.js +84 -1
- package/dist/packages/server/src/api.js.map +1 -1
- package/dist/packages/server/src/app.js +26 -1
- package/dist/packages/server/src/app.js.map +1 -1
- package/dist/packages/server/src/builtin.d.ts +4 -3
- package/dist/packages/server/src/builtin.js.map +1 -1
- package/dist/packages/server/src/events/runtime.js +17 -10
- package/dist/packages/server/src/events/runtime.js.map +1 -1
- package/dist/packages/server/src/events/store.d.ts +2 -2
- package/dist/packages/server/src/index.d.ts +3 -3
- package/dist/packages/server/src/index.js +32 -22
- package/dist/packages/server/src/index.js.map +1 -1
- package/dist/packages/server/src/service.d.ts +1113 -14
- package/dist/packages/server/src/service.js +266 -21
- package/dist/packages/server/src/service.js.map +1 -1
- package/dist/packages/server/src/store.d.ts +43 -4
- package/dist/packages/server/src/store.js +79 -0
- package/dist/packages/server/src/store.js.map +1 -1
- package/package.json +4 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { brand } from "../../connector-kit/src/branding.js";
|
|
2
2
|
import { describeCertificate, newCertificate } from "../../connector-kit/src/certificate.js";
|
|
3
3
|
import { signInNonce, signState, verifyState } from "./consent.js";
|
|
4
|
-
import { effectiveCapabilities, pickDisplay, providerSettings, tokenRolesFor, } from "../../connector-kit/src/connector.js";
|
|
4
|
+
import { effectiveCapabilities, parseConnector, pickDisplay, pickLabels, providerSettings, tokenRolesFor, } from "../../connector-kit/src/connector.js";
|
|
5
5
|
import { credentialAad, hashAgentKey, looksLikeAgentKey } from "./secrets.js";
|
|
6
6
|
import { ProviderChanged } from "./store.js";
|
|
7
7
|
import { configSites } from "../../../connectors/sharepoint-files/vendor.js";
|
|
@@ -211,12 +211,142 @@ export function createService(deps) {
|
|
|
211
211
|
if (s.confirm)
|
|
212
212
|
confirmable.set(s.confirm, provider.id);
|
|
213
213
|
const sharedClientId = (id) => providers.get(id)?.client.approval?.clientId ?? null;
|
|
214
|
-
|
|
214
|
+
// A loaded app (built in or from a folder), the same for every organization.
|
|
215
|
+
function loaded(connectorId) {
|
|
215
216
|
const found = apps.get(connectorId);
|
|
216
217
|
if (!found)
|
|
217
218
|
throw new InvalidInput(`Unknown app ${connectorId}`);
|
|
218
219
|
return found;
|
|
219
220
|
}
|
|
221
|
+
// The one lookup of an app for an organization: a loaded app, else one of its own. A loaded
|
|
222
|
+
// app whose id one of the organization's own already has is neither: the connection kept
|
|
223
|
+
// under that id holds a credential entered for the organization's app, never to be sent to
|
|
224
|
+
// the deployment's.
|
|
225
|
+
async function app(org, connectorId) {
|
|
226
|
+
const found = apps.get(connectorId);
|
|
227
|
+
const row = await store.getInternalApp(org, connectorId);
|
|
228
|
+
if (found && row)
|
|
229
|
+
throw new InvalidInput(shadowed(connectorId));
|
|
230
|
+
if (found)
|
|
231
|
+
return found;
|
|
232
|
+
if (!row)
|
|
233
|
+
throw new InvalidInput(`Unknown app ${connectorId}`);
|
|
234
|
+
return internal(row);
|
|
235
|
+
}
|
|
236
|
+
// Every app the organization has: the loaded ones and its own. One of its own that no longer
|
|
237
|
+
// loads (the deployment now has its id, or a newer release reads it differently) is left out
|
|
238
|
+
// and logged, rather than cutting off the organization's other apps; GET internal-apps names
|
|
239
|
+
// the problem.
|
|
240
|
+
async function orgApps(org) {
|
|
241
|
+
const all = new Map(apps);
|
|
242
|
+
for (const row of await store.listInternalApps(org)) {
|
|
243
|
+
if (apps.has(row.id)) {
|
|
244
|
+
all.delete(row.id);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
all.set(row.id, internal(row));
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
if (!(error instanceof InvalidInput))
|
|
252
|
+
throw error;
|
|
253
|
+
console.error(`[agent-connect] internal app ${row.id} of ${org}: ${error.message}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return all;
|
|
257
|
+
}
|
|
258
|
+
const reserved = new Set([...(deps.reservedIds ?? []), ...apps.keys(), "agents"]);
|
|
259
|
+
const loadedTools = new Set(deps.apps.flatMap((a) => a.toolset.names));
|
|
260
|
+
// Built internal apps by organization and id, kept while their definition is unchanged.
|
|
261
|
+
const built = new Map();
|
|
262
|
+
function internal(row) {
|
|
263
|
+
if (apps.has(row.id))
|
|
264
|
+
throw new InvalidInput(shadowed(row.id));
|
|
265
|
+
const json = JSON.stringify(row.definition);
|
|
266
|
+
const cacheKey = `${row.organizationId}/${row.id}`;
|
|
267
|
+
const hit = built.get(cacheKey);
|
|
268
|
+
if (hit?.json === json)
|
|
269
|
+
return hit.app;
|
|
270
|
+
const made = defined(row.definition, row.id);
|
|
271
|
+
// A tool the deployment added later would sit twice in an agent's list.
|
|
272
|
+
const clash = made.toolset.names.filter((t) => loadedTools.has(t));
|
|
273
|
+
if (clash.length)
|
|
274
|
+
throw new InvalidInput(`This deployment now has its own tool named ${clash.join(", ")}, so this app is not used. Rename its tools and save it again.`);
|
|
275
|
+
built.set(cacheKey, { json, app: made });
|
|
276
|
+
return made;
|
|
277
|
+
}
|
|
278
|
+
function withProblem(row) {
|
|
279
|
+
try {
|
|
280
|
+
internal(row);
|
|
281
|
+
return { ...row, problem: null };
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
if (!(error instanceof InvalidInput))
|
|
285
|
+
throw error;
|
|
286
|
+
return { ...row, problem: error.message };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// A definition parsed exactly as a connector.json file is, then built; every problem in
|
|
290
|
+
// plain words.
|
|
291
|
+
function defined(raw, id) {
|
|
292
|
+
if (!deps.internalApp)
|
|
293
|
+
throw new InvalidInput("This deployment does not take internal apps");
|
|
294
|
+
let connector;
|
|
295
|
+
try {
|
|
296
|
+
connector = parseConnector(raw, "definition");
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
throw new InvalidInput(plainParseError(error.message));
|
|
300
|
+
}
|
|
301
|
+
if (connector.id !== id)
|
|
302
|
+
throw new InvalidInput(`The definition's id is ${connector.id}, but it is saved as ${id}`);
|
|
303
|
+
if (!connector.http && !connector.soap && !connector.odata && !connector.sql)
|
|
304
|
+
throw new InvalidInput("An internal app needs an http, soap, odata or sql block");
|
|
305
|
+
if (connector.events)
|
|
306
|
+
throw new InvalidInput("Internal apps cannot start agents from events yet: remove events");
|
|
307
|
+
if (connector.signsInAs)
|
|
308
|
+
throw new InvalidInput("Internal apps sign in with their own connection: remove signsInAs");
|
|
309
|
+
try {
|
|
310
|
+
return deps.internalApp(connector);
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
throw new InvalidInput(error.message);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// What replacing a connected app's definition does: a change to how it signs in or where it
|
|
317
|
+
// reaches (form fields, accountKey, the http, soap, odata or sql block but its tools) removes
|
|
318
|
+
// the connection and its grants, since the credential was entered for the old one. Otherwise
|
|
319
|
+
// the ceiling and grants are cut to the capabilities the new definition still has, and a
|
|
320
|
+
// grant whose limits no longer fit it, or left with nothing, is removed.
|
|
321
|
+
function replaceChange(previous, next, connection, grants) {
|
|
322
|
+
if (!connection)
|
|
323
|
+
return { kind: "none" };
|
|
324
|
+
if (!previous || signIn(previous) !== signIn(next))
|
|
325
|
+
return { kind: "disconnect", reason: "its sign-in or address changed" };
|
|
326
|
+
const ceiling = next.capabilities
|
|
327
|
+
.filter((c) => c.required || connection.capabilities.includes(c.id))
|
|
328
|
+
.map((c) => c.id);
|
|
329
|
+
if (!ceiling.length)
|
|
330
|
+
return { kind: "disconnect", reason: "none of its capabilities is left" };
|
|
331
|
+
const kept = [];
|
|
332
|
+
const removed = [];
|
|
333
|
+
for (const g of grants) {
|
|
334
|
+
const capabilities = ceiling.filter((id) => g.capabilities.includes(id) || next.capabilities.find((c) => c.id === id).required);
|
|
335
|
+
let limits = null;
|
|
336
|
+
try {
|
|
337
|
+
limits = validateLimits(next, g.limits);
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
if (!(error instanceof InvalidInput))
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
if (limits && capabilities.length)
|
|
344
|
+
kept.push({ agentId: g.agentId, capabilities, limits });
|
|
345
|
+
else
|
|
346
|
+
removed.push(g.agentId);
|
|
347
|
+
}
|
|
348
|
+
return { kind: "keep", ceiling, grants: kept, removed };
|
|
349
|
+
}
|
|
220
350
|
function providerApp(id) {
|
|
221
351
|
const found = providers.get(id);
|
|
222
352
|
if (!found)
|
|
@@ -224,7 +354,7 @@ export function createService(deps) {
|
|
|
224
354
|
return found;
|
|
225
355
|
}
|
|
226
356
|
function approvalFor(connectorId) {
|
|
227
|
-
const target =
|
|
357
|
+
const target = loaded(connectorId);
|
|
228
358
|
const approval = target.connector.provider
|
|
229
359
|
? providerApp(target.connector.provider).client.approval
|
|
230
360
|
: undefined;
|
|
@@ -376,12 +506,17 @@ export function createService(deps) {
|
|
|
376
506
|
const results = [];
|
|
377
507
|
for (const p of probes(connector)) {
|
|
378
508
|
if ("check" in p) {
|
|
379
|
-
const { values, warning } = await vendor.checks[p.check](conn, {
|
|
509
|
+
const { values, warning, labels } = await vendor.checks[p.check](conn, {
|
|
380
510
|
expected: tokenRolesFor(connector, ceiling, conn.config),
|
|
381
511
|
});
|
|
382
512
|
// The vendor's own warning says more than the generic "nothing yet".
|
|
383
513
|
const shown = warning || (values.length === 0 ? p.ifEmpty : undefined);
|
|
384
|
-
results.push({
|
|
514
|
+
results.push({
|
|
515
|
+
label: p.label,
|
|
516
|
+
values,
|
|
517
|
+
...(shown ? { warning: shown } : {}),
|
|
518
|
+
...(labels ? { labels } : {}),
|
|
519
|
+
});
|
|
385
520
|
continue;
|
|
386
521
|
}
|
|
387
522
|
// {name} in a probe path is the connection's config value, e.g. the signed-in user's id.
|
|
@@ -399,20 +534,92 @@ export function createService(deps) {
|
|
|
399
534
|
label: p.label,
|
|
400
535
|
values,
|
|
401
536
|
...(values.length === 0 && p.ifEmpty ? { warning: p.ifEmpty } : {}),
|
|
537
|
+
...(p.name ? { labels: pickLabels(res.data, p.show, p.name, p.match) } : {}),
|
|
402
538
|
});
|
|
403
539
|
}
|
|
404
540
|
return results;
|
|
405
541
|
}
|
|
406
542
|
return {
|
|
407
543
|
connectors: () => deps.apps.map(({ connector }) => connector),
|
|
544
|
+
// Every app the organization can connect: the deployment's and its own.
|
|
545
|
+
async connectorsFor(org) {
|
|
546
|
+
return [...(await orgApps(org)).values()].map(({ connector }) => connector);
|
|
547
|
+
},
|
|
548
|
+
// The organization's own apps as saved, each with what keeps it from loading, if anything.
|
|
549
|
+
async listInternalApps(org) {
|
|
550
|
+
return (await store.listInternalApps(org)).map(withProblem);
|
|
551
|
+
},
|
|
552
|
+
async getInternalApp(org, id) {
|
|
553
|
+
const row = await store.getInternalApp(org, id);
|
|
554
|
+
return row ? withProblem(row) : null;
|
|
555
|
+
},
|
|
556
|
+
// Creates or replaces one of the organization's own apps (see replaceChange for what a
|
|
557
|
+
// replace does to its connection and grants), audited in the same transaction.
|
|
558
|
+
async saveInternalApp(org, id, definition, by) {
|
|
559
|
+
if (reserved.has(id))
|
|
560
|
+
throw new InvalidInput(`${id} is the id of one of this deployment's apps. Choose another id.`);
|
|
561
|
+
const next = defined(definition, id).connector;
|
|
562
|
+
const others = await orgApps(org);
|
|
563
|
+
others.delete(id);
|
|
564
|
+
const taken = next.capabilities
|
|
565
|
+
.flatMap((c) => c.tools)
|
|
566
|
+
.filter((t) => loadedTools.has(t) || [...others.values()].some((a) => a.toolset.names.includes(t)));
|
|
567
|
+
if (taken.length)
|
|
568
|
+
throw new InvalidInput(`Another app already has a tool named ${taken.join(", ")}. Tool names must be unique: rename them.`);
|
|
569
|
+
const { row, created, change } = await store.saveInternalApp({ organizationId: org, id, definition, by }, (previous, connection, grants) => {
|
|
570
|
+
let before = null;
|
|
571
|
+
try {
|
|
572
|
+
before = previous ? parseConnector(previous, "definition") : null;
|
|
573
|
+
}
|
|
574
|
+
catch {
|
|
575
|
+
// A stored definition that no longer parses cannot vouch for the connection.
|
|
576
|
+
}
|
|
577
|
+
return replaceChange(before, next, connection, grants);
|
|
578
|
+
}, (isNew, c) => ({
|
|
579
|
+
organizationId: org,
|
|
580
|
+
agentId: null,
|
|
581
|
+
agentName: by,
|
|
582
|
+
connectorId: id,
|
|
583
|
+
tool: `admin:${isNew ? "create" : "replace"}_internal_app`,
|
|
584
|
+
target: changeTarget(c),
|
|
585
|
+
decision: "allowed",
|
|
586
|
+
reason: null,
|
|
587
|
+
vendorStatus: null,
|
|
588
|
+
}));
|
|
589
|
+
return {
|
|
590
|
+
app: withProblem(row),
|
|
591
|
+
connector: next,
|
|
592
|
+
created,
|
|
593
|
+
disconnected: change.kind === "disconnect",
|
|
594
|
+
grantsRemoved: change.kind === "keep" ? change.removed : [],
|
|
595
|
+
};
|
|
596
|
+
},
|
|
597
|
+
// Removes one of the organization's own apps with its connection and every grant of it.
|
|
598
|
+
async deleteInternalApp(org, id, by) {
|
|
599
|
+
const deleted = await store.deleteInternalApp(org, id, {
|
|
600
|
+
organizationId: org,
|
|
601
|
+
agentId: null,
|
|
602
|
+
agentName: by,
|
|
603
|
+
connectorId: id,
|
|
604
|
+
tool: "admin:delete_internal_app",
|
|
605
|
+
target: `${id}, with its connection and every agent's access to it`,
|
|
606
|
+
decision: "allowed",
|
|
607
|
+
reason: null,
|
|
608
|
+
vendorStatus: null,
|
|
609
|
+
});
|
|
610
|
+
if (deleted)
|
|
611
|
+
built.delete(`${org}/${id}`);
|
|
612
|
+
return deleted;
|
|
613
|
+
},
|
|
408
614
|
providers: () => [...providers.values()].map(({ provider, client }) => ({
|
|
409
615
|
...provider,
|
|
410
616
|
// The deployment offers approving its own shared app instead of registering one.
|
|
411
617
|
approval: Boolean(client.approval),
|
|
412
618
|
})),
|
|
413
619
|
async listConnections(org) {
|
|
620
|
+
const known = await orgApps(org);
|
|
414
621
|
return (await store.listConnections(org))
|
|
415
|
-
.filter((r) =>
|
|
622
|
+
.filter((r) => known.has(r.connectorId))
|
|
416
623
|
.map(publicConnection);
|
|
417
624
|
},
|
|
418
625
|
async listProviders(org) {
|
|
@@ -429,7 +636,8 @@ export function createService(deps) {
|
|
|
429
636
|
return store.listConfirmed(org);
|
|
430
637
|
},
|
|
431
638
|
async setConfirmed(org, step, by, on) {
|
|
432
|
-
const owner = confirmable.get(step)
|
|
639
|
+
const owner = confirmable.get(step) ??
|
|
640
|
+
[...(await orgApps(org)).values()].find(({ connector }) => [...connector.setup, ...connector.afterConnect].some((s) => s.confirm === step))?.connector.id;
|
|
433
641
|
if (!owner)
|
|
434
642
|
throw new InvalidInput(`No setup step ${step} to confirm`);
|
|
435
643
|
await store.setConfirmed(org, step, by, on);
|
|
@@ -437,7 +645,7 @@ export function createService(deps) {
|
|
|
437
645
|
},
|
|
438
646
|
// Guided apps: the admin's form fields. Verified before anything is stored.
|
|
439
647
|
async connect(input) {
|
|
440
|
-
return save(app(input.connectorId), input);
|
|
648
|
+
return save(await app(input.organizationId, input.connectorId), input);
|
|
441
649
|
},
|
|
442
650
|
// The provider's own sign-in, shared by its apps. Proven before it is stored. Reaching
|
|
443
651
|
// another account than before disconnects its apps.
|
|
@@ -642,7 +850,7 @@ export function createService(deps) {
|
|
|
642
850
|
// not mark the connection broken and cut every agent off. A recorded check that changes the
|
|
643
851
|
// connection's status is audited under `by`, the admin who asked for it.
|
|
644
852
|
async check(org, connectorId, { record = true, by } = {}) {
|
|
645
|
-
const target = app(connectorId);
|
|
853
|
+
const target = await app(org, connectorId);
|
|
646
854
|
const row = await store.getConnection(org, connectorId);
|
|
647
855
|
if (!row)
|
|
648
856
|
return null;
|
|
@@ -680,7 +888,7 @@ export function createService(deps) {
|
|
|
680
888
|
},
|
|
681
889
|
// Sites added after connecting, the whole list at once (adding or removing one).
|
|
682
890
|
async setSites(org, connectorId, raw) {
|
|
683
|
-
const { connector, vendor } = app(connectorId);
|
|
891
|
+
const { connector, vendor } = await app(org, connectorId);
|
|
684
892
|
if (!connector.siteAccess || !vendor.sites)
|
|
685
893
|
throw new InvalidInput(`${connector.name} has no sites to add`);
|
|
686
894
|
const row = await store.getConnection(org, connectorId);
|
|
@@ -696,7 +904,7 @@ export function createService(deps) {
|
|
|
696
904
|
},
|
|
697
905
|
// Whether Microsoft gave the app each added site (or the one asked about), read live.
|
|
698
906
|
async siteAccess(org, connectorId, only) {
|
|
699
|
-
const { connector, vendor } = app(connectorId);
|
|
907
|
+
const { connector, vendor } = await app(org, connectorId);
|
|
700
908
|
if (!connector.siteAccess || !vendor.sites)
|
|
701
909
|
throw new InvalidInput(`${connector.name} has no sites to check`);
|
|
702
910
|
const row = await store.getConnection(org, connectorId);
|
|
@@ -717,7 +925,7 @@ export function createService(deps) {
|
|
|
717
925
|
// A check of an address no key holds yet is recorded, so lookups of other mailboxes in the
|
|
718
926
|
// tenant leave a trace.
|
|
719
927
|
async agentAccess(org, connectorId, value, by) {
|
|
720
|
-
const { connector, vendor } = app(connectorId);
|
|
928
|
+
const { connector, vendor } = await app(org, connectorId);
|
|
721
929
|
const access = connector.agentAccess;
|
|
722
930
|
if (!access)
|
|
723
931
|
throw new InvalidInput(`${connector.name} has no agent address to check`);
|
|
@@ -782,11 +990,11 @@ export function createService(deps) {
|
|
|
782
990
|
},
|
|
783
991
|
// The saved ceiling, or null when the app is not connected.
|
|
784
992
|
async setCeiling(org, connectorId, ceiling) {
|
|
785
|
-
const caps = validateCapabilities(app(connectorId).connector, ceiling);
|
|
993
|
+
const caps = validateCapabilities((await app(org, connectorId)).connector, ceiling);
|
|
786
994
|
return (await store.setCeiling(org, connectorId, caps)) ? caps : null;
|
|
787
995
|
},
|
|
788
996
|
async setGrant(org, agentId, connectorId, capabilities, limits) {
|
|
789
|
-
const { connector, vendor } = app(connectorId);
|
|
997
|
+
const { connector, vendor } = await app(org, connectorId);
|
|
790
998
|
const caps = validateCapabilities(connector, capabilities);
|
|
791
999
|
const checked = validateLimits(connector, limits);
|
|
792
1000
|
const row = await store.getConnection(org, connectorId);
|
|
@@ -821,7 +1029,7 @@ export function createService(deps) {
|
|
|
821
1029
|
/** A call with the organization's own connection, no agent's (an event poll or a webhook
|
|
822
1030
|
* registration). Null when the app is not connected. */
|
|
823
1031
|
async appCall(org, connectorId, request) {
|
|
824
|
-
const target = app(connectorId);
|
|
1032
|
+
const target = await app(org, connectorId);
|
|
825
1033
|
const row = await store.getConnection(org, connectorId);
|
|
826
1034
|
if (!row || row.status !== "connected")
|
|
827
1035
|
return null;
|
|
@@ -835,16 +1043,17 @@ export function createService(deps) {
|
|
|
835
1043
|
const agent = await store.agentByKeyHash(hashAgentKey(rawKey));
|
|
836
1044
|
if (!agent || agent.status !== "active")
|
|
837
1045
|
return null;
|
|
838
|
-
const [grants, connections, providerRows] = await Promise.all([
|
|
1046
|
+
const [grants, connections, providerRows, known] = await Promise.all([
|
|
839
1047
|
store.grantsFor(agent.id),
|
|
840
1048
|
store.listConnections(agent.organizationId),
|
|
841
1049
|
store.listProviders(agent.organizationId),
|
|
1050
|
+
orgApps(agent.organizationId),
|
|
842
1051
|
]);
|
|
843
|
-
const needsBot = grants.some((g) =>
|
|
1052
|
+
const needsBot = grants.some((g) => known.get(g.connectorId)?.connector.signsInAs);
|
|
844
1053
|
if (needsBot && !deps.bots)
|
|
845
1054
|
throw new Error("An app signs in as the agent's bot, but bots are not loaded");
|
|
846
1055
|
const bot = needsBot ? await deps.bots.botForAgent(agent.organizationId, agent.id) : null;
|
|
847
|
-
const toolsets = sessionToolsets(agent, grants, connections, providerRows, bot);
|
|
1056
|
+
const toolsets = sessionToolsets(known, agent, grants, connections, providerRows, bot);
|
|
848
1057
|
const granted = new Set(toolsets.flatMap((t) => [...t.allowed]));
|
|
849
1058
|
return {
|
|
850
1059
|
agent,
|
|
@@ -854,7 +1063,7 @@ export function createService(deps) {
|
|
|
854
1063
|
async refuseUngranted(tool, args) {
|
|
855
1064
|
if (granted.has(tool))
|
|
856
1065
|
return;
|
|
857
|
-
const owner =
|
|
1066
|
+
const owner = [...known.values()].find((a) => a.toolset.names.includes(tool));
|
|
858
1067
|
if (!owner)
|
|
859
1068
|
return;
|
|
860
1069
|
await store.audit({
|
|
@@ -872,9 +1081,9 @@ export function createService(deps) {
|
|
|
872
1081
|
};
|
|
873
1082
|
},
|
|
874
1083
|
};
|
|
875
|
-
function sessionToolsets(agent, grants, connections, providerRows, bot) {
|
|
1084
|
+
function sessionToolsets(known, agent, grants, connections, providerRows, bot) {
|
|
876
1085
|
return grants.flatMap((grant) => {
|
|
877
|
-
const target =
|
|
1086
|
+
const target = known.get(grant.connectorId);
|
|
878
1087
|
const row = connections.find((c) => c.connectorId === grant.connectorId);
|
|
879
1088
|
if (!target || !row || row.status !== "connected")
|
|
880
1089
|
return [];
|
|
@@ -888,6 +1097,7 @@ export function createService(deps) {
|
|
|
888
1097
|
return [];
|
|
889
1098
|
let conn = null;
|
|
890
1099
|
const ctx = {
|
|
1100
|
+
agentId: agent.id,
|
|
891
1101
|
limits: grant.limits,
|
|
892
1102
|
config: provider ? { ...provider.config, ...row.config } : row.config,
|
|
893
1103
|
call: (request) => {
|
|
@@ -915,4 +1125,39 @@ export function createService(deps) {
|
|
|
915
1125
|
});
|
|
916
1126
|
}
|
|
917
1127
|
}
|
|
1128
|
+
// The parser's message (z.prettifyError: "✖ problem" lines, each followed by "→ at path") as
|
|
1129
|
+
// plain sentences.
|
|
1130
|
+
export function plainParseError(message) {
|
|
1131
|
+
const lines = message
|
|
1132
|
+
.replace(/^definition: /, "")
|
|
1133
|
+
.split("\n")
|
|
1134
|
+
.map((l) => l.trim())
|
|
1135
|
+
.filter(Boolean);
|
|
1136
|
+
const out = [];
|
|
1137
|
+
for (const line of lines) {
|
|
1138
|
+
if (line.startsWith("→ at "))
|
|
1139
|
+
out[out.length - 1] += ` (at ${line.slice(5)})`;
|
|
1140
|
+
else
|
|
1141
|
+
out.push(line.replace(/^✖\s*/, ""));
|
|
1142
|
+
}
|
|
1143
|
+
return `This definition does not load: ${out.join("; ")}`;
|
|
1144
|
+
}
|
|
1145
|
+
const shadowed = (id) => `This deployment now has its own app with the id ${id}, so neither is used in this organization. Save this one under another id, then delete it here, which also removes its connection.`;
|
|
1146
|
+
// How a definition signs in and where it reaches, compared across a replace.
|
|
1147
|
+
function signIn(c) {
|
|
1148
|
+
const block = c.http ?? c.soap ?? c.odata ?? c.sql;
|
|
1149
|
+
const { tools: _, ...reach } = (block ?? {});
|
|
1150
|
+
return JSON.stringify({
|
|
1151
|
+
fields: c.fields.map((f) => [f.id, Boolean(f.secret), f.options?.map((o) => o.value) ?? null]),
|
|
1152
|
+
accountKey: c.accountKey,
|
|
1153
|
+
reach,
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
function changeTarget(change) {
|
|
1157
|
+
if (change.kind === "none")
|
|
1158
|
+
return "not connected";
|
|
1159
|
+
if (change.kind === "disconnect")
|
|
1160
|
+
return `disconnected: ${change.reason}`;
|
|
1161
|
+
return `capabilities ${change.ceiling.join(", ")}${change.removed.length ? `; access removed for agents ${change.removed.join(", ")}` : ""}`;
|
|
1162
|
+
}
|
|
918
1163
|
//# sourceMappingURL=service.js.map
|