@arbium/enterprise-agent-connect 0.2.0 → 0.2.1-dev.7b4e802

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 (68) hide show
  1. package/README.md +10 -3
  2. package/dist/connectors/adestra/connector.json +336 -0
  3. package/dist/connectors/adobe-admin-console/connector.json +212 -0
  4. package/dist/connectors/argo-cd/connector.json +210 -0
  5. package/dist/connectors/automox/connector.json +376 -0
  6. package/dist/connectors/churnzero/connector.json +238 -0
  7. package/dist/connectors/confluence/connector.json +168 -0
  8. package/dist/connectors/cvent/connector.json +287 -0
  9. package/dist/connectors/datadog/connector.json +188 -0
  10. package/dist/connectors/drata/connector.json +447 -0
  11. package/dist/connectors/druva/connector.json +258 -0
  12. package/dist/connectors/forsta-decipher/connector.json +164 -0
  13. package/dist/connectors/gitlab/connector.json +263 -0
  14. package/dist/connectors/grafana/connector.json +162 -0
  15. package/dist/connectors/http/index.d.ts +5 -1
  16. package/dist/connectors/http/index.js +62 -8
  17. package/dist/connectors/http/index.js.map +1 -1
  18. package/dist/connectors/http/openapi.d.ts +62 -0
  19. package/dist/connectors/http/openapi.js +342 -0
  20. package/dist/connectors/http/openapi.js.map +1 -0
  21. package/dist/connectors/intercom/connector.json +249 -0
  22. package/dist/connectors/lusha/connector.json +59 -0
  23. package/dist/connectors/lusha/index.d.ts +8 -0
  24. package/dist/connectors/lusha/index.js +7 -0
  25. package/dist/connectors/lusha/index.js.map +1 -0
  26. package/dist/connectors/lusha/tools.d.ts +2 -0
  27. package/dist/connectors/lusha/tools.js +113 -0
  28. package/dist/connectors/lusha/tools.js.map +1 -0
  29. package/dist/connectors/lusha/vendor.d.ts +4 -0
  30. package/dist/connectors/lusha/vendor.js +72 -0
  31. package/dist/connectors/lusha/vendor.js.map +1 -0
  32. package/dist/connectors/odata/index.js +12 -5
  33. package/dist/connectors/odata/index.js.map +1 -1
  34. package/dist/connectors/pagerduty/connector.json +247 -0
  35. package/dist/connectors/qualys/connector.json +271 -0
  36. package/dist/connectors/safebase/connector.json +221 -0
  37. package/dist/connectors/soap/index.js +4 -3
  38. package/dist/connectors/soap/index.js.map +1 -1
  39. package/dist/connectors/sonarqube/connector.json +165 -0
  40. package/dist/connectors/sprout-social/connector.json +158 -0
  41. package/dist/connectors/synthesia/connector.json +113 -0
  42. package/dist/connectors/wordpress/connector.json +203 -0
  43. package/dist/connectors/zephyr/connector.json +192 -0
  44. package/dist/packages/connector-kit/src/connector.d.ts +45 -7
  45. package/dist/packages/connector-kit/src/connector.js +113 -10
  46. package/dist/packages/connector-kit/src/connector.js.map +1 -1
  47. package/dist/packages/connector-kit/src/oauth.d.ts +2 -0
  48. package/dist/packages/connector-kit/src/oauth.js +12 -4
  49. package/dist/packages/connector-kit/src/oauth.js.map +1 -1
  50. package/dist/packages/server/migrations/0008_internal_apps.sql +16 -0
  51. package/dist/packages/server/migrations/rollback/to-legacy.sql +2 -0
  52. package/dist/packages/server/src/api.d.ts +2490 -277
  53. package/dist/packages/server/src/api.js +82 -1
  54. package/dist/packages/server/src/api.js.map +1 -1
  55. package/dist/packages/server/src/app.js +26 -1
  56. package/dist/packages/server/src/app.js.map +1 -1
  57. package/dist/packages/server/src/builtin.d.ts +3 -0
  58. package/dist/packages/server/src/builtin.js +32 -0
  59. package/dist/packages/server/src/builtin.js.map +1 -1
  60. package/dist/packages/server/src/index.js +27 -18
  61. package/dist/packages/server/src/index.js.map +1 -1
  62. package/dist/packages/server/src/service.d.ts +1113 -0
  63. package/dist/packages/server/src/service.js +257 -19
  64. package/dist/packages/server/src/service.js.map +1 -1
  65. package/dist/packages/server/src/store.d.ts +41 -1
  66. package/dist/packages/server/src/store.js +79 -0
  67. package/dist/packages/server/src/store.js.map +1 -1
  68. package/package.json +1 -1
@@ -0,0 +1,249 @@
1
+ {
2
+ "id": "intercom",
3
+ "name": "Intercom",
4
+ "description": "Let agents read client conversations in Intercom, look up the person and company behind them, leave internal notes for your team and assign conversations. If you allow it, they can also reply to clients.",
5
+ "logo": "/brand/registry/intercom.svg",
6
+ "identity": "The access token of an app in your Intercom workspace. It acts as the workspace, with the permissions you tick on the app, so keep them to the ones below.",
7
+ "accountKey": "host",
8
+ "connectionKind": "guided",
9
+ "setup": [
10
+ {
11
+ "title": "Create an app for agents",
12
+ "body": "You need an Intercom admin for this. An app made in your own workspace gets an access token for that workspace straight away.",
13
+ "link": {
14
+ "label": "Intercom: authentication",
15
+ "href": "https://developers.intercom.com/docs/build-an-integration/learn-more/authentication"
16
+ },
17
+ "click": [
18
+ "Open the Intercom Developer Hub (app.intercom.com/a/apps/_/developer-hub) and select New app.",
19
+ "Name it Agents, pick your workspace, then Create app."
20
+ ]
21
+ },
22
+ {
23
+ "title": "Set its permissions and copy the token",
24
+ "body": "Tick only the permissions below, then copy the access token for the form below.",
25
+ "link": {
26
+ "label": "Intercom: permissions",
27
+ "href": "https://developers.intercom.com/docs/build-an-integration/learn-more/authentication/oauth-scopes"
28
+ },
29
+ "click": [
30
+ "In the app, go to Configure, then Authentication, and select Edit.",
31
+ "Tick only the permissions below, then Save.",
32
+ "Copy the Access token shown on the same page."
33
+ ],
34
+ "copy": ["{scope}"]
35
+ }
36
+ ],
37
+ "afterConnect": [],
38
+ "fields": [
39
+ {
40
+ "id": "host",
41
+ "label": "API address",
42
+ "help": "api.intercom.io, or api.eu.intercom.io or api.au.intercom.io if your workspace is hosted in Europe or Australia.",
43
+ "placeholder": "api.intercom.io",
44
+ "secret": false
45
+ },
46
+ {
47
+ "id": "access_token",
48
+ "label": "Access token",
49
+ "secret": true
50
+ }
51
+ ],
52
+ "capabilities": [
53
+ {
54
+ "id": "read",
55
+ "label": "Read conversations",
56
+ "description": "List and read conversations, read a person's contact record, look up a company (its plan and custom attributes, such as what it is entitled to) and its people, and list your teammates.",
57
+ "required": true,
58
+ "note": "Reaches every conversation, person and company in the workspace. Read-only.",
59
+ "scopes": ["Read conversations", "Read and list users and companies", "Read admins"],
60
+ "permissions": [],
61
+ "tools": [
62
+ "intercom_list_conversations",
63
+ "intercom_get_conversation",
64
+ "intercom_get_contact",
65
+ "intercom_find_company",
66
+ "intercom_list_company_contacts",
67
+ "intercom_list_admins"
68
+ ]
69
+ },
70
+ {
71
+ "id": "triage",
72
+ "label": "Add notes and assign",
73
+ "description": "Leave an internal note on a conversation, which clients never see, and assign a conversation to a teammate or team.",
74
+ "scopes": ["Write conversations"],
75
+ "permissions": [],
76
+ "tools": ["intercom_add_note", "intercom_assign_conversation"]
77
+ },
78
+ {
79
+ "id": "reply",
80
+ "label": "Reply to clients",
81
+ "description": "Reply to a conversation as a teammate. The client sees the reply.",
82
+ "destructive": true,
83
+ "note": "Sends messages to your clients.",
84
+ "scopes": ["Write conversations"],
85
+ "permissions": [],
86
+ "tools": ["intercom_reply"]
87
+ }
88
+ ],
89
+ "limits": [],
90
+ "verify": {
91
+ "method": "GET",
92
+ "path": "/me",
93
+ "label": "Intercom workspace",
94
+ "show": "app.name"
95
+ },
96
+ "revoke": "In the Intercom Developer Hub, open the app, go to Configure, then Authentication, and regenerate the access token, or delete the app. Agents lose access to Intercom at once.",
97
+ "http": {
98
+ "baseUrl": "https://{host}",
99
+ "allowedHosts": ["api.intercom.io", "api.eu.intercom.io", "api.au.intercom.io"],
100
+ "auth": { "type": "bearer", "token": "access_token" },
101
+ "headers": { "Intercom-Version": "2.14" },
102
+ "tools": [
103
+ {
104
+ "name": "intercom_list_conversations",
105
+ "description": "List conversations, most recently updated first.",
106
+ "method": "GET",
107
+ "path": "/conversations",
108
+ "params": {
109
+ "per_page": { "type": "integer", "description": "Conversations per page, up to 150", "in": "query", "default": 20 },
110
+ "starting_after": { "type": "string", "description": "The next page's cursor, from the previous answer", "in": "query", "required": false }
111
+ },
112
+ "result": {
113
+ "items": "conversations",
114
+ "fields": [
115
+ "id",
116
+ "title",
117
+ "state",
118
+ "open",
119
+ "created_at",
120
+ "updated_at",
121
+ "waiting_since",
122
+ "admin_assignee_id",
123
+ "team_assignee_id",
124
+ "source.author.name",
125
+ "source.author.email",
126
+ "source.subject",
127
+ "contacts.contacts.id",
128
+ "tags.tags.name"
129
+ ]
130
+ },
131
+ "paging": { "param": "starting_after", "body": "pages.next.starting_after" }
132
+ },
133
+ {
134
+ "name": "intercom_get_conversation",
135
+ "description": "Read one conversation with its messages, as plain text.",
136
+ "method": "GET",
137
+ "path": "/conversations/{conversation_id}",
138
+ "params": {
139
+ "conversation_id": { "type": "string", "description": "The conversation id", "in": "path" },
140
+ "display_as": { "type": "string", "description": "Message format", "in": "query", "enum": ["plaintext"], "default": "plaintext" }
141
+ },
142
+ "result": {
143
+ "fields": [
144
+ "id",
145
+ "title",
146
+ "state",
147
+ "created_at",
148
+ "admin_assignee_id",
149
+ "team_assignee_id",
150
+ "custom_attributes",
151
+ "source.author.name",
152
+ "source.author.email",
153
+ "source.subject",
154
+ "source.body",
155
+ "contacts.contacts.id",
156
+ "conversation_parts.conversation_parts.part_type",
157
+ "conversation_parts.conversation_parts.body",
158
+ "conversation_parts.conversation_parts.created_at",
159
+ "conversation_parts.conversation_parts.author.name",
160
+ "conversation_parts.conversation_parts.author.type"
161
+ ]
162
+ }
163
+ },
164
+ {
165
+ "name": "intercom_get_contact",
166
+ "description": "Read one person's contact record: name, email, companies and custom attributes.",
167
+ "method": "GET",
168
+ "path": "/contacts/{contact_id}",
169
+ "params": {
170
+ "contact_id": { "type": "string", "description": "The contact id (from a conversation's contacts)", "in": "path" }
171
+ },
172
+ "result": {
173
+ "fields": ["id", "role", "name", "email", "phone", "external_id", "custom_attributes", "companies.data.id", "location.country", "last_seen_at"]
174
+ }
175
+ },
176
+ {
177
+ "name": "intercom_find_company",
178
+ "description": "Find a company by your own company id or by name: its plan, size and custom attributes, such as what it is entitled to.",
179
+ "method": "GET",
180
+ "path": "/companies",
181
+ "params": {
182
+ "company_id": { "type": "string", "description": "Your own id for the company", "in": "query", "required": false },
183
+ "name": { "type": "string", "description": "The company's name", "in": "query", "required": false }
184
+ }
185
+ },
186
+ {
187
+ "name": "intercom_list_company_contacts",
188
+ "description": "List the people at a company.",
189
+ "method": "GET",
190
+ "path": "/companies/{id}/contacts",
191
+ "params": {
192
+ "id": { "type": "string", "description": "Intercom's id for the company (its id field, not company_id)", "in": "path" }
193
+ },
194
+ "result": { "items": "data", "fields": ["id", "name", "email", "role"] }
195
+ },
196
+ {
197
+ "name": "intercom_list_admins",
198
+ "description": "List your teammates (admins), with their ids, emails and whether they are away. Use an id to assign a conversation or to write as that teammate.",
199
+ "method": "GET",
200
+ "path": "/admins",
201
+ "result": { "items": "admins", "fields": ["id", "name", "email", "job_title", "away_mode_enabled", "team_ids"] }
202
+ },
203
+ {
204
+ "name": "intercom_add_note",
205
+ "description": "Leave an internal note on a conversation. Clients never see notes.",
206
+ "method": "POST",
207
+ "path": "/conversations/{conversation_id}/reply",
208
+ "params": {
209
+ "conversation_id": { "type": "string", "description": "The conversation id", "in": "path" },
210
+ "admin_id": { "type": "string", "description": "The teammate the note is from", "in": "body" },
211
+ "body": { "type": "string", "description": "The note, as text or simple HTML", "in": "body" },
212
+ "message_type": { "type": "string", "description": "Always a note", "in": "body", "enum": ["note"], "default": "note" },
213
+ "type": { "type": "string", "description": "Always from a teammate", "in": "body", "enum": ["admin"], "default": "admin" }
214
+ },
215
+ "result": { "fields": ["id", "state", "admin_assignee_id"] }
216
+ },
217
+ {
218
+ "name": "intercom_assign_conversation",
219
+ "description": "Assign a conversation to a teammate or a team, with an optional note.",
220
+ "method": "POST",
221
+ "path": "/conversations/{conversation_id}/parts",
222
+ "params": {
223
+ "conversation_id": { "type": "string", "description": "The conversation id", "in": "path" },
224
+ "admin_id": { "type": "string", "description": "The teammate doing the assigning", "in": "body" },
225
+ "assignee_id": { "type": "string", "description": "The teammate or team to assign it to", "in": "body" },
226
+ "type": { "type": "string", "description": "Whether assignee_id is a teammate or a team", "in": "body", "enum": ["admin", "team"] },
227
+ "body": { "type": "string", "description": "An optional note with the assignment", "in": "body", "required": false },
228
+ "message_type": { "type": "string", "description": "Always an assignment", "in": "body", "enum": ["assignment"], "default": "assignment" }
229
+ },
230
+ "result": { "fields": ["id", "state", "admin_assignee_id", "team_assignee_id"] }
231
+ },
232
+ {
233
+ "name": "intercom_reply",
234
+ "description": "Reply to a conversation as a teammate. The client sees the reply.",
235
+ "method": "POST",
236
+ "path": "/conversations/{conversation_id}/reply",
237
+ "destructive": true,
238
+ "params": {
239
+ "conversation_id": { "type": "string", "description": "The conversation id", "in": "path" },
240
+ "admin_id": { "type": "string", "description": "The teammate the reply is from", "in": "body" },
241
+ "body": { "type": "string", "description": "The reply, as text or simple HTML", "in": "body" },
242
+ "message_type": { "type": "string", "description": "Always a reply", "in": "body", "enum": ["comment"], "default": "comment" },
243
+ "type": { "type": "string", "description": "Always from a teammate", "in": "body", "enum": ["admin"], "default": "admin" }
244
+ },
245
+ "result": { "fields": ["id", "state", "admin_assignee_id"] }
246
+ }
247
+ ]
248
+ }
249
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "id": "lusha",
3
+ "name": "Lusha",
4
+ "description": "Let agents look up business contacts and companies in Lusha: job title, work email and phone, company size, industry and funding. For account research, call lists and matching event delegates to sponsors.",
5
+ "logo": "/brand/registry/lusha.svg",
6
+ "identity": "A Lusha API key. Every lookup that reveals an email or phone number spends your company's Lusha credits, so give the key its own credit limit.",
7
+ "accountKey": "host",
8
+ "connectionKind": "guided",
9
+ "setup": [
10
+ {
11
+ "title": "Create an API key with a credit limit",
12
+ "body": "You need a Lusha admin or manager for this, so the key can carry a credit limit of its own. The key acts as the person who made it and spends from your company's shared credits. Lusha shows the key on the API page; copy it for the form below.",
13
+ "link": {
14
+ "label": "Lusha: manage API keys",
15
+ "href": "https://dashboard.lusha.com/api/manage-api-keys"
16
+ },
17
+ "click": [
18
+ "In Lusha, open API & connectors in the left sidebar, then the Manage API Keys tab.",
19
+ "Create a key for agents and set its credit limit to what agents may spend.",
20
+ "Copy the key."
21
+ ],
22
+ "copy": [
23
+ "curl -s -H \"api_key: $LUSHA_API_KEY\" https://api.lusha.com/v3/account/usage"
24
+ ],
25
+ "shell": "bash"
26
+ }
27
+ ],
28
+ "afterConnect": [],
29
+ "fields": [{ "id": "api_key", "label": "API key", "secret": true }],
30
+ "capabilities": [
31
+ {
32
+ "id": "contacts",
33
+ "label": "Look up contacts",
34
+ "description": "Find one person by email, LinkedIn address, Lusha id, or name and company: job title, seniority, department, start date and previous job, location, work email and phone.",
35
+ "required": true,
36
+ "note": "Each search, and each email or phone number revealed, spends Lusha credits, up to the key's credit limit.",
37
+ "scopes": [],
38
+ "permissions": [],
39
+ "tools": ["lusha_find_contact"]
40
+ },
41
+ {
42
+ "id": "companies",
43
+ "label": "Look up companies",
44
+ "description": "Find one company by domain, name or Lusha id: size, revenue, industry, location, description and funding.",
45
+ "note": "Spends Lusha credits.",
46
+ "scopes": [],
47
+ "permissions": [],
48
+ "tools": ["lusha_find_company"]
49
+ }
50
+ ],
51
+ "limits": [],
52
+ "verify": {
53
+ "method": "GET",
54
+ "path": "/v3/account/usage",
55
+ "label": "Lusha credits remaining",
56
+ "show": "credits.remaining"
57
+ },
58
+ "revoke": "In Lusha, open API & connectors, then Manage API Keys, and delete the key. Agents lose access to Lusha at once."
59
+ }
@@ -0,0 +1,8 @@
1
+ declare const _default: {
2
+ id: string;
3
+ create: () => {
4
+ vendor: import("../../packages/connector-kit/src/types.js").Vendor;
5
+ toolset: import("../../packages/connector-kit/src/common.js").Toolset;
6
+ };
7
+ };
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ import { lushaTools } from "./tools.js";
2
+ import { lushaVendor } from "./vendor.js";
3
+ export default {
4
+ id: "lusha",
5
+ create: () => ({ vendor: lushaVendor(), toolset: lushaTools }),
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../connectors/lusha/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,eAAe;IACb,EAAE,EAAE,OAAO;IACX,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;CACrC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type Toolset } from "../../packages/connector-kit/src/common.js";
2
+ export declare const lushaTools: Toolset;
@@ -0,0 +1,113 @@
1
+ import { z } from "zod";
2
+ import { argumentsTarget, audited, Denied, json, VendorFailed, } from "../../packages/connector-kit/src/common.js";
3
+ import { lushaError } from "./vendor.js";
4
+ const text = z.string().trim().min(1).max(300);
5
+ async function first(ctx, mark, path, body) {
6
+ const res = await ctx.call({ method: "POST", path, body });
7
+ mark(res.status);
8
+ if (res.status < 200 || res.status >= 300)
9
+ throw new VendorFailed(res.status, lushaError(res));
10
+ const data = (res.data ?? {});
11
+ const found = data.results?.[0];
12
+ return { found, credits: data.billing?.creditsCharged ?? null };
13
+ }
14
+ const pick = (o, keys) => o ? Object.fromEntries(keys.filter((k) => o[k] != null).map((k) => [k, o[k]])) : null;
15
+ export const lushaTools = {
16
+ names: ["lusha_find_contact", "lusha_find_company"],
17
+ register(server, ctx, allowed) {
18
+ if (allowed.has("lusha_find_contact"))
19
+ server.registerTool("lusha_find_contact", {
20
+ description: "Find one person in Lusha. Send email, or linkedin_url, or person_id, or first_name and last_name with company_name or company_domain. Spends credits for the search and for each email and phone revealed.",
21
+ inputSchema: z
22
+ .object({
23
+ email: text.optional(),
24
+ linkedin_url: text.optional(),
25
+ person_id: text.optional(),
26
+ first_name: text.optional(),
27
+ last_name: text.optional(),
28
+ company_name: text.optional(),
29
+ company_domain: text.optional(),
30
+ reveal: z
31
+ .array(z.enum(["emails", "phones"]))
32
+ .default(["emails", "phones"])
33
+ .describe("What to reveal; each one spends credits. Empty reveals neither."),
34
+ })
35
+ .strict(),
36
+ annotations: { readOnlyHint: true, openWorldHint: true },
37
+ }, (args) => audited(ctx, "lusha_find_contact", argumentsTarget(args), async (mark) => {
38
+ const named = args.first_name && args.last_name && (args.company_name || args.company_domain);
39
+ if (!args.email && !args.linkedin_url && !args.person_id && !named)
40
+ throw new Denied("Send email, linkedin_url or person_id, or first_name and last_name with company_name or company_domain");
41
+ const contact = Object.fromEntries(Object.entries({
42
+ id: args.person_id,
43
+ email: args.email,
44
+ linkedinUrl: args.linkedin_url,
45
+ firstName: args.first_name,
46
+ lastName: args.last_name,
47
+ companyName: args.company_name,
48
+ companyDomain: args.company_domain,
49
+ }).filter(([, v]) => v !== undefined));
50
+ const { found, credits } = await first(ctx, mark, "/v3/contacts/search-and-enrich", {
51
+ contacts: [contact],
52
+ reveal: args.reveal,
53
+ });
54
+ if (!found || found.error)
55
+ return json({ found: false, error: found?.error ?? null, credits });
56
+ return json({
57
+ found: true,
58
+ credits,
59
+ person_id: found.id ?? null,
60
+ name: found.fullName ?? null,
61
+ job_title: found.jobTitle ?? null,
62
+ company: found.company ?? null,
63
+ location: pick(found.location, ["city", "state", "country"]),
64
+ emails: (found.emails ?? []).map((e) => pick(e, ["email", "type", "confidence"])),
65
+ phones: (found.phones ?? []).map((p) => pick(p, ["number", "type", "doNotCall"])),
66
+ linkedin: found.socialLinks?.linkedin ?? null,
67
+ previous_jobs: (found.previousEmployment ?? []).slice(0, 5),
68
+ });
69
+ }));
70
+ if (allowed.has("lusha_find_company"))
71
+ server.registerTool("lusha_find_company", {
72
+ description: "Find one company in Lusha by domain, name or Lusha id: size, revenue, industry, location, description and funding. Spends credits.",
73
+ inputSchema: z
74
+ .object({
75
+ domain: text.optional(),
76
+ name: text.optional(),
77
+ company_id: text.optional(),
78
+ })
79
+ .strict(),
80
+ annotations: { readOnlyHint: true, openWorldHint: true },
81
+ }, (args) => audited(ctx, "lusha_find_company", argumentsTarget(args), async (mark) => {
82
+ if (!args.domain && !args.name && !args.company_id)
83
+ throw new Denied("Send domain, name or company_id");
84
+ const company = Object.fromEntries(Object.entries({ id: args.company_id, name: args.name, domain: args.domain }).filter(([, v]) => v !== undefined));
85
+ const { found, credits } = await first(ctx, mark, "/v3/companies/search-and-enrich", {
86
+ companies: [company],
87
+ });
88
+ if (!found || found.error)
89
+ return json({ found: false, error: found?.error ?? null, credits });
90
+ return json({
91
+ found: true,
92
+ credits,
93
+ ...pick(found, [
94
+ "id",
95
+ "name",
96
+ "domain",
97
+ "companyType",
98
+ "yearFounded",
99
+ "employeeCount",
100
+ "revenueRange",
101
+ "industry",
102
+ "subIndustry",
103
+ "location",
104
+ "funding",
105
+ "technologies",
106
+ ]),
107
+ description: typeof found.description === "string" ? found.description.slice(0, 1000) : null,
108
+ linkedin: found.socialLinks?.linkedin ?? null,
109
+ });
110
+ }));
111
+ },
112
+ };
113
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../connectors/lusha/tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type Fetch, type Vendor, type VendorResponse } from "../../packages/connector-kit/src/types.js";
2
+ export declare const LUSHA_HOST = "api.lusha.com";
3
+ export declare function lushaError(res: VendorResponse): string;
4
+ export declare function lushaVendor(unguarded?: Fetch, wait?: (s: number) => Promise<void>): Vendor;
@@ -0,0 +1,72 @@
1
+ import { retryAfter, vendorFetch } from "../../packages/connector-kit/src/fetch.js";
2
+ import { vendorError } from "../../packages/connector-kit/src/oauth.js";
3
+ import { ConnectError, } from "../../packages/connector-kit/src/types.js";
4
+ // A Lusha API key in the api_key header, against Lusha's v3 API. v2 (GET /v2/person and
5
+ // /v2/company) stops answering on November 30, 2026, and its replacements take a JSON list of
6
+ // people or companies, which is why this app has code rather than an http block.
7
+ // Sources: docs.lusha.com/openapi.json and the V3 migration guide (read November 1, 2026).
8
+ export const LUSHA_HOST = "api.lusha.com";
9
+ const BASE = `https://${LUSHA_HOST}`;
10
+ export function lushaError(res) {
11
+ if (res.status === 402)
12
+ return "Lusha says the key is out of credits";
13
+ if (res.status === 429) {
14
+ const after = res.data?.retryAfter;
15
+ const wait = typeof after === "number" ? ` Try again in ${after} seconds.` : "";
16
+ return `Lusha is limiting how often the app may call it.${wait}`;
17
+ }
18
+ const detail = vendorError(res.data);
19
+ return `Lusha answered ${res.status}${detail ? `: ${detail}` : ""}`;
20
+ }
21
+ export function lushaVendor(unguarded = fetch, wait) {
22
+ const fetchImpl = vendorFetch("Lusha", unguarded, wait);
23
+ async function send(key, request) {
24
+ const res = await fetchImpl(`${BASE}${request.path}`, {
25
+ method: request.method,
26
+ headers: {
27
+ api_key: key,
28
+ accept: "application/json",
29
+ ...(request.body === undefined ? {} : { "content-type": "application/json" }),
30
+ },
31
+ body: request.body === undefined ? undefined : JSON.stringify(request.body),
32
+ });
33
+ const raw = await res.text();
34
+ const headers = Object.fromEntries(res.headers);
35
+ if (res.status === 429)
36
+ return {
37
+ status: 429,
38
+ data: { retryAfter: retryAfter(res.headers.get("retry-after")) },
39
+ headers,
40
+ };
41
+ return { status: res.status, data: raw ? parse(raw) : null, headers };
42
+ }
43
+ return {
44
+ async connect(fields) {
45
+ const key = fields.api_key?.trim() ?? "";
46
+ if (!key)
47
+ throw new ConnectError("Enter the Lusha API key");
48
+ const res = await send(key, { method: "GET", path: "/v3/account/usage" });
49
+ if (res.status === 401)
50
+ throw new ConnectError("Lusha did not accept the API key");
51
+ if (res.status === 403)
52
+ throw new ConnectError(`Lusha refused the key. Ask Lusha to turn on API access for your plan. (${lushaError(res)})`);
53
+ if (res.status !== 200)
54
+ throw new ConnectError(`Lusha sign-in failed. ${lushaError(res)}`);
55
+ return {
56
+ config: { host: LUSHA_HOST },
57
+ secret: { api_key: key },
58
+ accountLabel: `API key ending ${key.slice(-4)}`,
59
+ };
60
+ },
61
+ call: (connection, request) => send(connection.secret.api_key ?? "", request),
62
+ };
63
+ }
64
+ function parse(raw) {
65
+ try {
66
+ return JSON.parse(raw);
67
+ }
68
+ catch {
69
+ return raw;
70
+ }
71
+ }
72
+ //# sourceMappingURL=vendor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendor.js","sourceRoot":"","sources":["../../../connectors/lusha/vendor.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import { audited, Denied, json, listLimit, VendorFailed, } from "../../packages/connector-kit/src/common.js";
3
3
  import { clientCredentials, vendorError } from "../../packages/connector-kit/src/oauth.js";
4
4
  import { ConnectError, } from "../../packages/connector-kit/src/types.js";
5
- import { answerWithin, authHeaders, formValues, guardedFetch, MAX_BODY_BYTES, MAX_TARGET_BYTES, paramLimits, } from "../http/index.js";
5
+ import { answerWithin, authHeaders, formValues, connectionAddress, guardedFetch, MAX_BODY_BYTES, MAX_TARGET_BYTES, paramLimits, } from "../http/index.js";
6
6
  const DATETIME = z
7
7
  .string()
8
8
  .regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d{1,7})?)?(Z|[+-]\d{2}:\d{2})?)?$/);
@@ -125,23 +125,29 @@ function option(link, name) {
125
125
  }
126
126
  export function odataVendor(connector, fetchImpl = fetch) {
127
127
  const od = connector.odata;
128
- const root = new URL(od.serviceRoot);
129
- const prefix = root.pathname.replace(/\/+$/, "");
130
128
  const auth = od.auth;
131
- const safe = guardedFetch([root.origin, ...(auth.type === "oauth2" ? [new URL(auth.tokenUrl).origin] : [])], fetchImpl);
132
- const oauth = auth.type === "oauth2" ? clientCredentials(connector.name, safe) : null;
129
+ const tokenUrl = auth.type === "oauth2" ? auth.tokenUrl : null;
130
+ const addressOf = connectionAddress(connector, od.serviceRoot, od.allowedHosts, tokenUrl, fetchImpl);
131
+ // Only ever fetches the token, so one client serves every connection's address.
132
+ const oauth = tokenUrl
133
+ ? clientCredentials(connector.name, guardedFetch([new URL(tokenUrl).origin], fetchImpl))
134
+ : null;
133
135
  const creds = (c) => auth.type === "oauth2"
134
136
  ? {
135
137
  tokenUrl: auth.tokenUrl,
136
138
  clientId: c.config[auth.clientId],
137
139
  clientSecret: c.secret[auth.clientSecret],
138
140
  scope: auth.scope,
141
+ clientAuth: auth.clientAuth,
142
+ audience: auth.audience,
139
143
  }
140
144
  : null;
141
145
  const versionHeaders = od.version === "4"
142
146
  ? { "odata-version": "4.0", "odata-maxversion": "4.0" }
143
147
  : { dataserviceversion: "2.0", maxdataserviceversion: "2.0" };
144
148
  async function send(c, request) {
149
+ const { url: root, safe } = addressOf(c);
150
+ const prefix = root.pathname.replace(/\/+$/, "");
145
151
  // The path is the tool's, already encoded; it must stay under the service root.
146
152
  const url = new URL(`${root.origin}${prefix}${request.path}`);
147
153
  if (url.origin !== root.origin || !`${url.pathname}/`.startsWith(`${prefix}/`))
@@ -186,6 +192,7 @@ export function odataVendor(connector, fetchImpl = fetch) {
186
192
  return {
187
193
  async connect(fields) {
188
194
  const { config, secret } = formValues(connector, fields);
195
+ const root = addressOf({ config }).url;
189
196
  const conn = { config: { host: root.host, ...config }, secret };
190
197
  if (oauth)
191
198
  await oauth.signIn(creds(conn));