@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.
Files changed (127) hide show
  1. package/README.md +15 -5
  2. package/dist/connectors/dynamics-365/connector.json +16 -1
  3. package/dist/connectors/dynamics-365/index.d.ts +1 -4
  4. package/dist/connectors/dynamics-365/index.js.map +1 -1
  5. package/dist/connectors/entra/index.d.ts +1 -1
  6. package/dist/connectors/gong/vendor.d.ts +1 -1
  7. package/dist/connectors/gong/vendor.js +24 -32
  8. package/dist/connectors/gong/vendor.js.map +1 -1
  9. package/dist/connectors/http/index.d.ts +2 -6
  10. package/dist/connectors/http/index.js +4 -1
  11. package/dist/connectors/http/index.js.map +1 -1
  12. package/dist/connectors/http/openapi.d.ts +62 -0
  13. package/dist/connectors/http/openapi.js +342 -0
  14. package/dist/connectors/http/openapi.js.map +1 -0
  15. package/dist/connectors/intune/index.d.ts +1 -1
  16. package/dist/connectors/jamf/vendor.js +5 -1
  17. package/dist/connectors/jamf/vendor.js.map +1 -1
  18. package/dist/connectors/jira/connector.json +3 -3
  19. package/dist/connectors/jira/vendor.d.ts +1 -1
  20. package/dist/connectors/jira/vendor.js +8 -2
  21. package/dist/connectors/jira/vendor.js.map +1 -1
  22. package/dist/connectors/microsoft/client.d.ts +11 -3
  23. package/dist/connectors/microsoft/client.js +62 -8
  24. package/dist/connectors/microsoft/client.js.map +1 -1
  25. package/dist/connectors/netsuite/connector.json +4 -2
  26. package/dist/connectors/netsuite/vendor.d.ts +1 -1
  27. package/dist/connectors/netsuite/vendor.js +5 -6
  28. package/dist/connectors/netsuite/vendor.js.map +1 -1
  29. package/dist/connectors/odata/index.d.ts +1 -4
  30. package/dist/connectors/odata/index.js.map +1 -1
  31. package/dist/connectors/okta/connector.json +3 -2
  32. package/dist/connectors/okta/vendor.d.ts +1 -1
  33. package/dist/connectors/okta/vendor.js +7 -6
  34. package/dist/connectors/okta/vendor.js.map +1 -1
  35. package/dist/connectors/outlook-calendar/index.d.ts +1 -1
  36. package/dist/connectors/outlook-mail/index.d.ts +1 -1
  37. package/dist/connectors/salesforce/vendor.d.ts +1 -1
  38. package/dist/connectors/salesforce/vendor.js +9 -3
  39. package/dist/connectors/salesforce/vendor.js.map +1 -1
  40. package/dist/connectors/sap-s4hana/connector.json +1 -0
  41. package/dist/connectors/servicenow/vendor.d.ts +0 -1
  42. package/dist/connectors/servicenow/vendor.js +7 -10
  43. package/dist/connectors/servicenow/vendor.js.map +1 -1
  44. package/dist/connectors/sharepoint-files/connector.json +34 -3
  45. package/dist/connectors/sharepoint-files/documents.d.ts +7 -0
  46. package/dist/connectors/sharepoint-files/documents.js +212 -0
  47. package/dist/connectors/sharepoint-files/documents.js.map +1 -0
  48. package/dist/connectors/sharepoint-files/docx-worker.d.ts +7 -0
  49. package/dist/connectors/sharepoint-files/docx-worker.js +41 -0
  50. package/dist/connectors/sharepoint-files/docx-worker.js.map +1 -0
  51. package/dist/connectors/sharepoint-files/docx.d.ts +96 -0
  52. package/dist/connectors/sharepoint-files/docx.js +823 -0
  53. package/dist/connectors/sharepoint-files/docx.js.map +1 -0
  54. package/dist/connectors/sharepoint-files/index.d.ts +1 -4
  55. package/dist/connectors/sharepoint-files/index.js +2 -1
  56. package/dist/connectors/sharepoint-files/index.js.map +1 -1
  57. package/dist/connectors/sharepoint-files/ooxml.d.ts +19 -0
  58. package/dist/connectors/sharepoint-files/ooxml.js +179 -0
  59. package/dist/connectors/sharepoint-files/ooxml.js.map +1 -0
  60. package/dist/connectors/sharepoint-files/paging.d.ts +15 -0
  61. package/dist/connectors/sharepoint-files/paging.js +87 -0
  62. package/dist/connectors/sharepoint-files/paging.js.map +1 -0
  63. package/dist/connectors/sharepoint-files/parse-host.mjs +78 -0
  64. package/dist/connectors/sharepoint-files/pdf-worker.mjs +108 -0
  65. package/dist/connectors/sharepoint-files/pdf.d.ts +16 -0
  66. package/dist/connectors/sharepoint-files/pdf.js +19 -0
  67. package/dist/connectors/sharepoint-files/pdf.js.map +1 -0
  68. package/dist/connectors/sharepoint-files/sandbox.d.ts +19 -0
  69. package/dist/connectors/sharepoint-files/sandbox.js +176 -0
  70. package/dist/connectors/sharepoint-files/sandbox.js.map +1 -0
  71. package/dist/connectors/sharepoint-files/tools.d.ts +6 -0
  72. package/dist/connectors/sharepoint-files/tools.js +79 -4
  73. package/dist/connectors/sharepoint-files/tools.js.map +1 -1
  74. package/dist/connectors/slack/vendor.d.ts +1 -1
  75. package/dist/connectors/slack/vendor.js +32 -38
  76. package/dist/connectors/slack/vendor.js.map +1 -1
  77. package/dist/connectors/soap/index.d.ts +1 -4
  78. package/dist/connectors/sql/index.d.ts +1 -4
  79. package/dist/connectors/sql/index.js +2 -0
  80. package/dist/connectors/sql/index.js.map +1 -1
  81. package/dist/connectors/teams/index.d.ts +1 -1
  82. package/dist/connectors/teams/tools.d.ts +6 -1
  83. package/dist/connectors/teams/tools.js +3 -2
  84. package/dist/connectors/teams/tools.js.map +1 -1
  85. package/dist/connectors/workday/connector.json +6 -4
  86. package/dist/connectors/workday/vendor.d.ts +1 -1
  87. package/dist/connectors/workday/vendor.js +8 -4
  88. package/dist/connectors/workday/vendor.js.map +1 -1
  89. package/dist/packages/connector-kit/src/common.d.ts +4 -5
  90. package/dist/packages/connector-kit/src/common.js +4 -1
  91. package/dist/packages/connector-kit/src/common.js.map +1 -1
  92. package/dist/packages/connector-kit/src/connector.d.ts +37 -27
  93. package/dist/packages/connector-kit/src/connector.js +50 -11
  94. package/dist/packages/connector-kit/src/connector.js.map +1 -1
  95. package/dist/packages/connector-kit/src/fetch.d.ts +6 -0
  96. package/dist/packages/connector-kit/src/fetch.js +44 -0
  97. package/dist/packages/connector-kit/src/fetch.js.map +1 -0
  98. package/dist/packages/connector-kit/src/index.d.ts +3 -3
  99. package/dist/packages/connector-kit/src/index.js +1 -0
  100. package/dist/packages/connector-kit/src/index.js.map +1 -1
  101. package/dist/packages/connector-kit/src/oauth.d.ts +1 -1
  102. package/dist/packages/connector-kit/src/oauth.js +4 -2
  103. package/dist/packages/connector-kit/src/oauth.js.map +1 -1
  104. package/dist/packages/connector-kit/src/types.d.ts +4 -1
  105. package/dist/packages/connector-kit/src/types.js.map +1 -1
  106. package/dist/packages/server/migrations/0008_internal_apps.sql +16 -0
  107. package/dist/packages/server/migrations/rollback/to-legacy.sql +2 -0
  108. package/dist/packages/server/src/api.d.ts +2417 -253
  109. package/dist/packages/server/src/api.js +84 -1
  110. package/dist/packages/server/src/api.js.map +1 -1
  111. package/dist/packages/server/src/app.js +26 -1
  112. package/dist/packages/server/src/app.js.map +1 -1
  113. package/dist/packages/server/src/builtin.d.ts +4 -3
  114. package/dist/packages/server/src/builtin.js.map +1 -1
  115. package/dist/packages/server/src/events/runtime.js +17 -10
  116. package/dist/packages/server/src/events/runtime.js.map +1 -1
  117. package/dist/packages/server/src/events/store.d.ts +2 -2
  118. package/dist/packages/server/src/index.d.ts +3 -3
  119. package/dist/packages/server/src/index.js +32 -22
  120. package/dist/packages/server/src/index.js.map +1 -1
  121. package/dist/packages/server/src/service.d.ts +1113 -14
  122. package/dist/packages/server/src/service.js +266 -21
  123. package/dist/packages/server/src/service.js.map +1 -1
  124. package/dist/packages/server/src/store.d.ts +43 -4
  125. package/dist/packages/server/src/store.js +79 -0
  126. package/dist/packages/server/src/store.js.map +1 -1
  127. 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
- function app(connectorId) {
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 = app(connectorId);
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({ label: p.label, values, ...(shown ? { warning: shown } : {}) });
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) => apps.has(r.connectorId))
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) => apps.get(g.connectorId)?.connector.signsInAs);
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 = deps.apps.find((a) => a.toolset.names.includes(tool));
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 = apps.get(grant.connectorId);
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