@squadbase/vite-server 0.1.9-dev.d3c856d → 0.1.9-dev.e22f810
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/dist/cli/index.js +4607 -99
- package/dist/connectors/azure-sql.d.ts +5 -0
- package/dist/connectors/azure-sql.js +682 -0
- package/dist/connectors/clickup.d.ts +5 -0
- package/dist/connectors/clickup.js +850 -0
- package/dist/connectors/freshdesk.d.ts +5 -0
- package/dist/connectors/freshdesk.js +842 -0
- package/dist/connectors/freshsales.d.ts +5 -0
- package/dist/connectors/freshsales.js +867 -0
- package/dist/connectors/freshservice.d.ts +5 -0
- package/dist/connectors/freshservice.js +813 -0
- package/dist/connectors/github.d.ts +5 -0
- package/dist/connectors/github.js +963 -0
- package/dist/connectors/google-search-console-oauth.d.ts +5 -0
- package/dist/connectors/google-search-console-oauth.js +954 -0
- package/dist/connectors/jdbc.d.ts +5 -0
- package/dist/connectors/jdbc.js +21521 -0
- package/dist/connectors/monday.d.ts +5 -0
- package/dist/connectors/monday.js +853 -0
- package/dist/connectors/oracle.d.ts +5 -0
- package/dist/connectors/oracle.js +689 -0
- package/dist/connectors/semrush.d.ts +5 -0
- package/dist/connectors/semrush.js +825 -0
- package/dist/connectors/sqlserver.d.ts +5 -0
- package/dist/connectors/sqlserver.js +679 -0
- package/dist/connectors/supabase.d.ts +5 -0
- package/dist/connectors/supabase.js +582 -0
- package/dist/index.js +4607 -99
- package/dist/main.js +4607 -99
- package/dist/vite-plugin.js +4607 -99
- package/package.json +56 -2
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
// ../connectors/src/parameter-definition.ts
|
|
2
|
+
var ParameterDefinition = class {
|
|
3
|
+
slug;
|
|
4
|
+
name;
|
|
5
|
+
description;
|
|
6
|
+
envVarBaseKey;
|
|
7
|
+
type;
|
|
8
|
+
secret;
|
|
9
|
+
required;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.slug = config.slug;
|
|
12
|
+
this.name = config.name;
|
|
13
|
+
this.description = config.description;
|
|
14
|
+
this.envVarBaseKey = config.envVarBaseKey;
|
|
15
|
+
this.type = config.type;
|
|
16
|
+
this.secret = config.secret;
|
|
17
|
+
this.required = config.required;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get the parameter value from a ConnectorConnectionObject.
|
|
21
|
+
*/
|
|
22
|
+
getValue(connection2) {
|
|
23
|
+
const param = connection2.parameters.find(
|
|
24
|
+
(p) => p.parameterSlug === this.slug
|
|
25
|
+
);
|
|
26
|
+
if (!param || param.value == null) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Parameter "${this.slug}" not found or has no value in connection "${connection2.id}"`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return param.value;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Try to get the parameter value. Returns undefined if not found (for optional params).
|
|
35
|
+
*/
|
|
36
|
+
tryGetValue(connection2) {
|
|
37
|
+
const param = connection2.parameters.find(
|
|
38
|
+
(p) => p.parameterSlug === this.slug
|
|
39
|
+
);
|
|
40
|
+
if (!param || param.value == null) return void 0;
|
|
41
|
+
return param.value;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ../connectors/src/connectors/freshsales/parameters.ts
|
|
46
|
+
var parameters = {
|
|
47
|
+
bundleAlias: new ParameterDefinition({
|
|
48
|
+
slug: "bundle-alias",
|
|
49
|
+
name: "Freshsales Bundle Alias",
|
|
50
|
+
description: "Your Freshsales / Freshworks CRM bundle alias (the subdomain on `myfreshworks.com` \u2014 e.g. `acme` for `https://acme.myfreshworks.com`). Provide just the subdomain without `https://`.",
|
|
51
|
+
envVarBaseKey: "FRESHSALES_BUNDLE_ALIAS",
|
|
52
|
+
type: "text",
|
|
53
|
+
secret: false,
|
|
54
|
+
required: true
|
|
55
|
+
}),
|
|
56
|
+
apiKey: new ParameterDefinition({
|
|
57
|
+
slug: "api-key",
|
|
58
|
+
name: "Freshsales API Key",
|
|
59
|
+
description: "API key for Freshsales / Freshworks CRM. Find it in CRM \u2192 Personal Settings \u2192 API Settings. The key is sent in the `Authorization: Token token=<key>` header.",
|
|
60
|
+
envVarBaseKey: "FRESHSALES_API_KEY",
|
|
61
|
+
type: "text",
|
|
62
|
+
secret: true,
|
|
63
|
+
required: true
|
|
64
|
+
})
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ../connectors/src/connectors/freshsales/sdk/index.ts
|
|
68
|
+
function buildBaseUrl(bundleAlias) {
|
|
69
|
+
const trimmed = bundleAlias.trim().replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
70
|
+
const subdomain = trimmed.split(".")[0];
|
|
71
|
+
return `https://${subdomain}.myfreshworks.com/crm/sales/api`;
|
|
72
|
+
}
|
|
73
|
+
function createClient(params) {
|
|
74
|
+
const apiKey = params[parameters.apiKey.slug];
|
|
75
|
+
const bundle = params[parameters.bundleAlias.slug];
|
|
76
|
+
if (!apiKey) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`freshsales: missing required parameter: ${parameters.apiKey.slug}`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (!bundle) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`freshsales: missing required parameter: ${parameters.bundleAlias.slug}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
const baseUrl = buildBaseUrl(bundle);
|
|
87
|
+
const auth = `Token token=${apiKey}`;
|
|
88
|
+
function authHeaders(extra) {
|
|
89
|
+
const headers = new Headers(extra);
|
|
90
|
+
headers.set("Authorization", auth);
|
|
91
|
+
headers.set("Content-Type", "application/json");
|
|
92
|
+
headers.set("Accept", "application/json");
|
|
93
|
+
return headers;
|
|
94
|
+
}
|
|
95
|
+
async function assertOk(res, label) {
|
|
96
|
+
if (!res.ok) {
|
|
97
|
+
const body = await res.text().catch(() => "(unreadable body)");
|
|
98
|
+
throw new Error(
|
|
99
|
+
`freshsales ${label}: ${res.status} ${res.statusText} \u2014 ${body}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function buildQuery(base) {
|
|
104
|
+
const search = new URLSearchParams();
|
|
105
|
+
for (const [key, value] of Object.entries(base)) {
|
|
106
|
+
if (value !== void 0) search.set(key, String(value));
|
|
107
|
+
}
|
|
108
|
+
const qs = search.toString();
|
|
109
|
+
return qs ? `?${qs}` : "";
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
request(path2, init) {
|
|
113
|
+
const headers = new Headers(init?.headers);
|
|
114
|
+
headers.set("Authorization", auth);
|
|
115
|
+
if (!headers.has("Content-Type")) {
|
|
116
|
+
headers.set("Content-Type", "application/json");
|
|
117
|
+
}
|
|
118
|
+
if (!headers.has("Accept")) {
|
|
119
|
+
headers.set("Accept", "application/json");
|
|
120
|
+
}
|
|
121
|
+
return fetch(`${baseUrl}${path2}`, { ...init, headers });
|
|
122
|
+
},
|
|
123
|
+
async listContactViews() {
|
|
124
|
+
const res = await fetch(`${baseUrl}/contacts/filters`, {
|
|
125
|
+
method: "GET",
|
|
126
|
+
headers: authHeaders()
|
|
127
|
+
});
|
|
128
|
+
await assertOk(res, "listContactViews");
|
|
129
|
+
return await res.json();
|
|
130
|
+
},
|
|
131
|
+
async listContactsInView(viewId, options) {
|
|
132
|
+
const qs = buildQuery({
|
|
133
|
+
page: options?.page,
|
|
134
|
+
per_page: options?.per_page,
|
|
135
|
+
sort: options?.sort,
|
|
136
|
+
sort_type: options?.sort_type,
|
|
137
|
+
include: options?.include
|
|
138
|
+
});
|
|
139
|
+
const res = await fetch(
|
|
140
|
+
`${baseUrl}/contacts/view/${encodeURIComponent(String(viewId))}${qs}`,
|
|
141
|
+
{ method: "GET", headers: authHeaders() }
|
|
142
|
+
);
|
|
143
|
+
await assertOk(res, "listContactsInView");
|
|
144
|
+
return await res.json();
|
|
145
|
+
},
|
|
146
|
+
async getContact(contactId, options) {
|
|
147
|
+
const qs = buildQuery({ include: options?.include });
|
|
148
|
+
const res = await fetch(
|
|
149
|
+
`${baseUrl}/contacts/${encodeURIComponent(String(contactId))}${qs}`,
|
|
150
|
+
{ method: "GET", headers: authHeaders() }
|
|
151
|
+
);
|
|
152
|
+
await assertOk(res, "getContact");
|
|
153
|
+
return await res.json();
|
|
154
|
+
},
|
|
155
|
+
async createContact(contactData) {
|
|
156
|
+
const res = await fetch(`${baseUrl}/contacts`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: authHeaders(),
|
|
159
|
+
body: JSON.stringify({ contact: contactData })
|
|
160
|
+
});
|
|
161
|
+
await assertOk(res, "createContact");
|
|
162
|
+
return await res.json();
|
|
163
|
+
},
|
|
164
|
+
async updateContact(contactId, contactData) {
|
|
165
|
+
const res = await fetch(
|
|
166
|
+
`${baseUrl}/contacts/${encodeURIComponent(String(contactId))}`,
|
|
167
|
+
{
|
|
168
|
+
method: "PUT",
|
|
169
|
+
headers: authHeaders(),
|
|
170
|
+
body: JSON.stringify({ contact: contactData })
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
await assertOk(res, "updateContact");
|
|
174
|
+
return await res.json();
|
|
175
|
+
},
|
|
176
|
+
async listAccountViews() {
|
|
177
|
+
const res = await fetch(`${baseUrl}/sales_accounts/filters`, {
|
|
178
|
+
method: "GET",
|
|
179
|
+
headers: authHeaders()
|
|
180
|
+
});
|
|
181
|
+
await assertOk(res, "listAccountViews");
|
|
182
|
+
return await res.json();
|
|
183
|
+
},
|
|
184
|
+
async listAccountsInView(viewId, options) {
|
|
185
|
+
const qs = buildQuery({
|
|
186
|
+
page: options?.page,
|
|
187
|
+
per_page: options?.per_page,
|
|
188
|
+
include: options?.include
|
|
189
|
+
});
|
|
190
|
+
const res = await fetch(
|
|
191
|
+
`${baseUrl}/sales_accounts/view/${encodeURIComponent(String(viewId))}${qs}`,
|
|
192
|
+
{ method: "GET", headers: authHeaders() }
|
|
193
|
+
);
|
|
194
|
+
await assertOk(res, "listAccountsInView");
|
|
195
|
+
return await res.json();
|
|
196
|
+
},
|
|
197
|
+
async listDealViews() {
|
|
198
|
+
const res = await fetch(`${baseUrl}/deals/filters`, {
|
|
199
|
+
method: "GET",
|
|
200
|
+
headers: authHeaders()
|
|
201
|
+
});
|
|
202
|
+
await assertOk(res, "listDealViews");
|
|
203
|
+
return await res.json();
|
|
204
|
+
},
|
|
205
|
+
async listDealsInView(viewId, options) {
|
|
206
|
+
const qs = buildQuery({
|
|
207
|
+
page: options?.page,
|
|
208
|
+
per_page: options?.per_page,
|
|
209
|
+
include: options?.include
|
|
210
|
+
});
|
|
211
|
+
const res = await fetch(
|
|
212
|
+
`${baseUrl}/deals/view/${encodeURIComponent(String(viewId))}${qs}`,
|
|
213
|
+
{ method: "GET", headers: authHeaders() }
|
|
214
|
+
);
|
|
215
|
+
await assertOk(res, "listDealsInView");
|
|
216
|
+
return await res.json();
|
|
217
|
+
},
|
|
218
|
+
async getDeal(dealId, options) {
|
|
219
|
+
const qs = buildQuery({ include: options?.include });
|
|
220
|
+
const res = await fetch(
|
|
221
|
+
`${baseUrl}/deals/${encodeURIComponent(String(dealId))}${qs}`,
|
|
222
|
+
{ method: "GET", headers: authHeaders() }
|
|
223
|
+
);
|
|
224
|
+
await assertOk(res, "getDeal");
|
|
225
|
+
return await res.json();
|
|
226
|
+
},
|
|
227
|
+
async lookup(query) {
|
|
228
|
+
const qs = buildQuery({
|
|
229
|
+
q: query.q,
|
|
230
|
+
f: query.f,
|
|
231
|
+
entities: query.entities
|
|
232
|
+
});
|
|
233
|
+
const res = await fetch(`${baseUrl}/lookup${qs}`, {
|
|
234
|
+
method: "GET",
|
|
235
|
+
headers: authHeaders()
|
|
236
|
+
});
|
|
237
|
+
await assertOk(res, "lookup");
|
|
238
|
+
return await res.json();
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ../connectors/src/connector-onboarding.ts
|
|
244
|
+
var ConnectorOnboarding = class {
|
|
245
|
+
/** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
|
|
246
|
+
connectionSetupInstructions;
|
|
247
|
+
/** Phase 2: Data overview instructions */
|
|
248
|
+
dataOverviewInstructions;
|
|
249
|
+
constructor(config) {
|
|
250
|
+
this.connectionSetupInstructions = config.connectionSetupInstructions;
|
|
251
|
+
this.dataOverviewInstructions = config.dataOverviewInstructions;
|
|
252
|
+
}
|
|
253
|
+
getConnectionSetupPrompt(language) {
|
|
254
|
+
return this.connectionSetupInstructions?.[language] ?? null;
|
|
255
|
+
}
|
|
256
|
+
getDataOverviewInstructions(language) {
|
|
257
|
+
return this.dataOverviewInstructions[language];
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// ../connectors/src/connector-tool.ts
|
|
262
|
+
var ConnectorTool = class {
|
|
263
|
+
name;
|
|
264
|
+
description;
|
|
265
|
+
inputSchema;
|
|
266
|
+
outputSchema;
|
|
267
|
+
_execute;
|
|
268
|
+
constructor(config) {
|
|
269
|
+
this.name = config.name;
|
|
270
|
+
this.description = config.description;
|
|
271
|
+
this.inputSchema = config.inputSchema;
|
|
272
|
+
this.outputSchema = config.outputSchema;
|
|
273
|
+
this._execute = config.execute;
|
|
274
|
+
}
|
|
275
|
+
createTool(connections, config) {
|
|
276
|
+
return {
|
|
277
|
+
description: this.description,
|
|
278
|
+
inputSchema: this.inputSchema,
|
|
279
|
+
outputSchema: this.outputSchema,
|
|
280
|
+
execute: (input) => this._execute(input, connections, config)
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// ../connectors/src/connector-plugin.ts
|
|
286
|
+
var ConnectorPlugin = class _ConnectorPlugin {
|
|
287
|
+
slug;
|
|
288
|
+
authType;
|
|
289
|
+
name;
|
|
290
|
+
description;
|
|
291
|
+
iconUrl;
|
|
292
|
+
parameters;
|
|
293
|
+
releaseFlag;
|
|
294
|
+
proxyPolicy;
|
|
295
|
+
experimentalAttributes;
|
|
296
|
+
categories;
|
|
297
|
+
onboarding;
|
|
298
|
+
systemPrompt;
|
|
299
|
+
tools;
|
|
300
|
+
query;
|
|
301
|
+
checkConnection;
|
|
302
|
+
constructor(config) {
|
|
303
|
+
this.slug = config.slug;
|
|
304
|
+
this.authType = config.authType;
|
|
305
|
+
this.name = config.name;
|
|
306
|
+
this.description = config.description;
|
|
307
|
+
this.iconUrl = config.iconUrl;
|
|
308
|
+
this.parameters = config.parameters;
|
|
309
|
+
this.releaseFlag = config.releaseFlag;
|
|
310
|
+
this.proxyPolicy = config.proxyPolicy;
|
|
311
|
+
this.experimentalAttributes = config.experimentalAttributes;
|
|
312
|
+
this.categories = config.categories ?? [];
|
|
313
|
+
this.onboarding = config.onboarding;
|
|
314
|
+
this.systemPrompt = config.systemPrompt;
|
|
315
|
+
this.tools = config.tools;
|
|
316
|
+
this.query = config.query;
|
|
317
|
+
this.checkConnection = config.checkConnection;
|
|
318
|
+
}
|
|
319
|
+
get connectorKey() {
|
|
320
|
+
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Create tools for connections that belong to this connector.
|
|
324
|
+
* Filters connections by connectorKey internally.
|
|
325
|
+
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
326
|
+
*/
|
|
327
|
+
createTools(connections, config, opts) {
|
|
328
|
+
const myConnections = connections.filter(
|
|
329
|
+
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
330
|
+
);
|
|
331
|
+
const result = {};
|
|
332
|
+
for (const t of Object.values(this.tools)) {
|
|
333
|
+
const tool = t.createTool(myConnections, config);
|
|
334
|
+
const originalToModelOutput = tool.toModelOutput;
|
|
335
|
+
result[`${this.connectorKey}_${t.name}`] = {
|
|
336
|
+
...tool,
|
|
337
|
+
toModelOutput: async (options) => {
|
|
338
|
+
if (!originalToModelOutput) {
|
|
339
|
+
return opts.truncateOutput(options.output);
|
|
340
|
+
}
|
|
341
|
+
const modelOutput = await originalToModelOutput(options);
|
|
342
|
+
if (modelOutput.type === "text" || modelOutput.type === "json") {
|
|
343
|
+
return opts.truncateOutput(modelOutput.value);
|
|
344
|
+
}
|
|
345
|
+
return modelOutput;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
static deriveKey(slug, authType) {
|
|
352
|
+
if (authType) return `${slug}-${authType}`;
|
|
353
|
+
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
354
|
+
// user-password
|
|
355
|
+
"postgresql": "user-password",
|
|
356
|
+
"mysql": "user-password",
|
|
357
|
+
"clickhouse": "user-password",
|
|
358
|
+
"kintone": "user-password",
|
|
359
|
+
"squadbase-db": "user-password",
|
|
360
|
+
// service-account
|
|
361
|
+
"snowflake": "service-account",
|
|
362
|
+
"bigquery": "service-account",
|
|
363
|
+
"google-analytics": "service-account",
|
|
364
|
+
"google-calendar": "service-account",
|
|
365
|
+
"aws-athena": "service-account",
|
|
366
|
+
"redshift": "service-account",
|
|
367
|
+
// api-key
|
|
368
|
+
"databricks": "api-key",
|
|
369
|
+
"dbt": "api-key",
|
|
370
|
+
"airtable": "api-key",
|
|
371
|
+
"openai": "api-key",
|
|
372
|
+
"gemini": "api-key",
|
|
373
|
+
"anthropic": "api-key",
|
|
374
|
+
"wix-store": "api-key"
|
|
375
|
+
};
|
|
376
|
+
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
377
|
+
if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
|
|
378
|
+
return slug;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// ../connectors/src/auth-types.ts
|
|
383
|
+
var AUTH_TYPES = {
|
|
384
|
+
OAUTH: "oauth",
|
|
385
|
+
API_KEY: "api-key",
|
|
386
|
+
JWT: "jwt",
|
|
387
|
+
SERVICE_ACCOUNT: "service-account",
|
|
388
|
+
PAT: "pat",
|
|
389
|
+
USER_PASSWORD: "user-password"
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// ../connectors/src/lib/normalize-path.ts
|
|
393
|
+
function normalizeRequestPath(path2, basePathSegment) {
|
|
394
|
+
let p = path2.trim();
|
|
395
|
+
if (!p.startsWith("/")) p = "/" + p;
|
|
396
|
+
if (p === basePathSegment || p.startsWith(basePathSegment + "/")) {
|
|
397
|
+
p = p.slice(basePathSegment.length) || "/";
|
|
398
|
+
}
|
|
399
|
+
return p;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ../connectors/src/connectors/freshsales/setup.ts
|
|
403
|
+
var freshsalesOnboarding = new ConnectorOnboarding({
|
|
404
|
+
dataOverviewInstructions: {
|
|
405
|
+
en: `1. Call freshsales_request with GET /selector/owners to confirm credentials and discover sales team members.
|
|
406
|
+
2. Call freshsales_request with GET /contacts/filters to list available contact views (filters), then GET /contacts/view/{view_id} to fetch contacts in a view (paginated with \`page\` and \`per_page\`, default 25, max 100).
|
|
407
|
+
3. Repeat the same pattern for accounts (GET /sales_accounts/filters \u2192 /sales_accounts/view/{view_id}) and deals (GET /deals/filters \u2192 /deals/view/{view_id}).
|
|
408
|
+
4. To inspect deal pipeline metadata, call GET /selector/deal_pipelines and GET /selector/deal_stages.
|
|
409
|
+
5. Use GET /lookup to find specific records by attribute (e.g., look up a contact by email).`,
|
|
410
|
+
ja: `1. freshsales_request \u3067 GET /selector/owners \u3092\u547C\u3073\u51FA\u3057\u3001\u8A8D\u8A3C\u60C5\u5831\u306E\u78BA\u8A8D\u3068\u55B6\u696D\u30E1\u30F3\u30D0\u30FC\u306E\u4E00\u89A7\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002
|
|
411
|
+
2. freshsales_request \u3067 GET /contacts/filters \u3092\u547C\u3073\u51FA\u3057\u3066\u30D3\u30E5\u30FC\uFF08filters\uFF09\u4E00\u89A7\u3092\u53D6\u5F97\u3057\u3001GET /contacts/view/{view_id} \u3067\u5404\u30D3\u30E5\u30FC\u306E\u9023\u7D61\u5148\u3092\u53D6\u5F97\u3057\u307E\u3059\uFF08\`page\` \u3068 \`per_page\` \u3067\u30DA\u30FC\u30B8\u30F3\u30B0\u3001\u30C7\u30D5\u30A9\u30EB\u30C825\u3001\u6700\u5927100\uFF09\u3002
|
|
412
|
+
3. \u540C\u3058\u30D1\u30BF\u30FC\u30F3\u3092\u53D6\u5F15\u5148\uFF08GET /sales_accounts/filters \u2192 /sales_accounts/view/{view_id}\uFF09\u3001\u5546\u8AC7\uFF08GET /deals/filters \u2192 /deals/view/{view_id}\uFF09\u306B\u3082\u9069\u7528\u3057\u307E\u3059\u3002
|
|
413
|
+
4. \u5546\u8AC7\u30D1\u30A4\u30D7\u30E9\u30A4\u30F3\u306E\u30E1\u30BF\u60C5\u5831\u306F GET /selector/deal_pipelines \u304A\u3088\u3073 GET /selector/deal_stages \u3067\u53D6\u5F97\u3057\u307E\u3059\u3002
|
|
414
|
+
5. \u7279\u5B9A\u30EC\u30B3\u30FC\u30C9\u3092\u5C5E\u6027\u3067\u691C\u7D22\u3059\u308B\u306B\u306F GET /lookup \u3092\u4F7F\u7528\u3057\u307E\u3059\uFF08\u4F8B: \u30E1\u30FC\u30EB\u3067\u9023\u7D61\u5148\u3092\u691C\u7D22\uFF09\u3002`
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// ../connectors/src/connectors/freshsales/tools/request.ts
|
|
419
|
+
import { z } from "zod";
|
|
420
|
+
var BASE_PATH_SEGMENT = "/crm/sales/api";
|
|
421
|
+
var REQUEST_TIMEOUT_MS = 6e4;
|
|
422
|
+
function buildBaseUrl2(bundleAlias) {
|
|
423
|
+
const trimmed = bundleAlias.trim().replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
424
|
+
const subdomain = trimmed.split(".")[0];
|
|
425
|
+
return `https://${subdomain}.myfreshworks.com${BASE_PATH_SEGMENT}`;
|
|
426
|
+
}
|
|
427
|
+
var inputSchema = z.object({
|
|
428
|
+
toolUseIntent: z.string().optional().describe(
|
|
429
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
430
|
+
),
|
|
431
|
+
connectionId: z.string().describe("ID of the Freshsales connection to use"),
|
|
432
|
+
method: z.enum(["GET", "POST", "PUT", "DELETE"]).describe(
|
|
433
|
+
"HTTP method. GET for reading resources (including /lookup), POST for creating, PUT for updating, DELETE for removing."
|
|
434
|
+
),
|
|
435
|
+
path: z.string().describe(
|
|
436
|
+
"API path (e.g., '/contacts', '/contacts/filters', '/contacts/view/{view_id}', '/deals/{id}', '/lookup'). Append query parameters such as '?page=1&per_page=100'."
|
|
437
|
+
),
|
|
438
|
+
body: z.record(z.string(), z.unknown()).optional().describe(
|
|
439
|
+
'Request body (JSON). For creating a contact: { "contact": { "first_name": "...", "last_name": "...", "email": "..." } }. For lookup: { "q": "john@example.com", "f": "email", "entities": "contact" }.'
|
|
440
|
+
)
|
|
441
|
+
});
|
|
442
|
+
var outputSchema = z.discriminatedUnion("success", [
|
|
443
|
+
z.object({
|
|
444
|
+
success: z.literal(true),
|
|
445
|
+
status: z.number(),
|
|
446
|
+
data: z.union([
|
|
447
|
+
z.record(z.string(), z.unknown()),
|
|
448
|
+
z.array(z.unknown())
|
|
449
|
+
])
|
|
450
|
+
}),
|
|
451
|
+
z.object({
|
|
452
|
+
success: z.literal(false),
|
|
453
|
+
error: z.string()
|
|
454
|
+
})
|
|
455
|
+
]);
|
|
456
|
+
var requestTool = new ConnectorTool({
|
|
457
|
+
name: "request",
|
|
458
|
+
description: `Send authenticated requests to the Freshsales / Freshworks CRM REST API.
|
|
459
|
+
Authentication uses the \`Authorization: Token token=<API_KEY>\` header (handled automatically \u2014 note this is NOT HTTP Basic Auth like Freshdesk/Freshservice).
|
|
460
|
+
Provide the API path relative to the base URL (https://<bundle>.myfreshworks.com/crm/sales/api).
|
|
461
|
+
|
|
462
|
+
Common endpoints:
|
|
463
|
+
- GET /contacts/filters \u2014 List contact views (saved filters)
|
|
464
|
+
- GET /contacts/view/{view_id} \u2014 List contacts in a view (paginated)
|
|
465
|
+
- GET /contacts/{id} \u2014 Get a contact (use ?include=owner,creator,deals for related data)
|
|
466
|
+
- POST /contacts \u2014 Create a contact (body: { "contact": { ... } })
|
|
467
|
+
- PUT /contacts/{id} \u2014 Update a contact
|
|
468
|
+
- GET /sales_accounts/filters \u2014 List account views
|
|
469
|
+
- GET /sales_accounts/view/{view_id} \u2014 Accounts in a view
|
|
470
|
+
- GET /deals/filters \u2014 Deal views
|
|
471
|
+
- GET /deals/view/{view_id} \u2014 Deals in a view
|
|
472
|
+
- GET /deals/{id} \u2014 Get a deal
|
|
473
|
+
- POST /deals \u2014 Create a deal
|
|
474
|
+
- GET /leads/filters \u2014 Lead views (legacy / Sales Cloud only)
|
|
475
|
+
- GET /sales_activities \u2014 List sales activities
|
|
476
|
+
- GET /lookup?q=...&f=email&entities=contact \u2014 Look up a record by attribute
|
|
477
|
+
- GET /selector/owners \u2014 Available owners (sales reps)
|
|
478
|
+
- GET /selector/deal_pipelines \u2014 Deal pipelines
|
|
479
|
+
- GET /selector/deal_stages \u2014 Deal stages
|
|
480
|
+
- GET /selector/deal_types \u2014 Deal types
|
|
481
|
+
- GET /selector/lifecycle_stages \u2014 Lifecycle stages
|
|
482
|
+
|
|
483
|
+
Pagination: 1-indexed \`page\` + \`per_page\` (default 25, max 100). View endpoints return \`{ contacts/sales_accounts/deals: [...], meta: { total_pages, total } }\`.
|
|
484
|
+
|
|
485
|
+
Includes: most GET endpoints accept \`?include=\` with a comma-separated list (\`owner\`, \`creator\`, \`deals\`, \`contacts\`, etc.) to inline related records.`,
|
|
486
|
+
inputSchema,
|
|
487
|
+
outputSchema,
|
|
488
|
+
async execute({ connectionId, method, path: path2, body }, connections) {
|
|
489
|
+
const connection2 = connections.find((c) => c.id === connectionId);
|
|
490
|
+
if (!connection2) {
|
|
491
|
+
return {
|
|
492
|
+
success: false,
|
|
493
|
+
error: `Connection ${connectionId} not found`
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
console.log(
|
|
497
|
+
`[connector-request] freshsales/${connection2.name}: ${method} ${path2}`
|
|
498
|
+
);
|
|
499
|
+
try {
|
|
500
|
+
const apiKey = parameters.apiKey.getValue(connection2);
|
|
501
|
+
const bundle = parameters.bundleAlias.getValue(connection2);
|
|
502
|
+
const baseUrl = buildBaseUrl2(bundle);
|
|
503
|
+
const normalizedPath = normalizeRequestPath(path2, BASE_PATH_SEGMENT);
|
|
504
|
+
const url = `${baseUrl}${normalizedPath}`;
|
|
505
|
+
const controller = new AbortController();
|
|
506
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
507
|
+
try {
|
|
508
|
+
const response = await fetch(url, {
|
|
509
|
+
method,
|
|
510
|
+
headers: {
|
|
511
|
+
Authorization: `Token token=${apiKey}`,
|
|
512
|
+
"Content-Type": "application/json",
|
|
513
|
+
Accept: "application/json"
|
|
514
|
+
},
|
|
515
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
516
|
+
signal: controller.signal
|
|
517
|
+
});
|
|
518
|
+
const text = await response.text();
|
|
519
|
+
const data = text ? JSON.parse(text) : {};
|
|
520
|
+
if (!response.ok) {
|
|
521
|
+
const errObj = !Array.isArray(data) ? data : {};
|
|
522
|
+
const errorsField = errObj.errors;
|
|
523
|
+
const message = typeof errObj.message === "string" && errObj.message || (errorsField && typeof errorsField.message === "string" ? errorsField.message : null);
|
|
524
|
+
const errorMessage = message ? String(message) : `HTTP ${response.status} ${response.statusText}`;
|
|
525
|
+
return { success: false, error: errorMessage };
|
|
526
|
+
}
|
|
527
|
+
return { success: true, status: response.status, data };
|
|
528
|
+
} finally {
|
|
529
|
+
clearTimeout(timeout);
|
|
530
|
+
}
|
|
531
|
+
} catch (err) {
|
|
532
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
533
|
+
return { success: false, error: msg };
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
// ../connectors/src/connectors/freshsales/index.ts
|
|
539
|
+
var tools = { request: requestTool };
|
|
540
|
+
var freshsalesConnector = new ConnectorPlugin({
|
|
541
|
+
slug: "freshsales",
|
|
542
|
+
authType: AUTH_TYPES.API_KEY,
|
|
543
|
+
name: "Freshsales",
|
|
544
|
+
description: "Connect to Freshsales / Freshworks CRM for contact, account, and deal data via API key.",
|
|
545
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4UDOrFPM6wOFekbMVljjgl/4acc6060c3a1ff703980e6f4e76a3cd4/629b6c6f7c5cd817694c321f.png",
|
|
546
|
+
parameters,
|
|
547
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
548
|
+
categories: ["crm"],
|
|
549
|
+
onboarding: freshsalesOnboarding,
|
|
550
|
+
systemPrompt: {
|
|
551
|
+
en: `### Tools
|
|
552
|
+
|
|
553
|
+
- \`freshsales_request\`: The only way to call the Freshsales / Freshworks CRM REST API. Use it to list and update contacts, accounts, deals, leads, sales activities, and selector metadata. Authentication (\`Authorization: Token token=<API_KEY>\` header \u2014 note: NOT HTTP Basic Auth) and the base URL (https://<bundle>.myfreshworks.com/crm/sales/api) are configured automatically.
|
|
554
|
+
|
|
555
|
+
### Business Logic
|
|
556
|
+
|
|
557
|
+
The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
|
|
558
|
+
|
|
559
|
+
SDK methods (client created via \`connection(connectionId)\`):
|
|
560
|
+
- \`client.request(path, init?)\` \u2014 low-level authenticated fetch
|
|
561
|
+
- \`client.listContactViews()\` / \`client.listContactsInView(viewId, options?)\` \u2014 list contacts via a saved view
|
|
562
|
+
- \`client.getContact(contactId, options?)\` \u2014 get a single contact
|
|
563
|
+
- \`client.createContact(contactData)\` / \`client.updateContact(contactId, contactData)\` \u2014 write contacts
|
|
564
|
+
- \`client.listAccountViews()\` / \`client.listAccountsInView(viewId, options?)\` \u2014 sales accounts
|
|
565
|
+
- \`client.listDealViews()\` / \`client.listDealsInView(viewId, options?)\` / \`client.getDeal(dealId, options?)\` \u2014 deals
|
|
566
|
+
- \`client.lookup({ q, f, entities })\` \u2014 find a record by attribute (e.g. lookup contact by email)
|
|
567
|
+
|
|
568
|
+
\`\`\`ts
|
|
569
|
+
import type { Context } from "hono";
|
|
570
|
+
import { connection } from "@squadbase/vite-server/connectors/freshsales";
|
|
571
|
+
|
|
572
|
+
const fs = connection("<connectionId>");
|
|
573
|
+
|
|
574
|
+
export default async function handler(c: Context) {
|
|
575
|
+
const views = await fs.listDealViews();
|
|
576
|
+
const firstViewId = (views.filters[0] as { id: number }).id;
|
|
577
|
+
const { deals, meta } = await fs.listDealsInView(firstViewId, {
|
|
578
|
+
per_page: 100,
|
|
579
|
+
include: "owner,deal_stage,sales_account",
|
|
580
|
+
});
|
|
581
|
+
return c.json({ deals, totalPages: meta.total_pages });
|
|
582
|
+
}
|
|
583
|
+
\`\`\`
|
|
584
|
+
|
|
585
|
+
### Freshsales / Freshworks CRM REST API Reference
|
|
586
|
+
|
|
587
|
+
- Base URL: \`https://<bundle>.myfreshworks.com/crm/sales/api\`
|
|
588
|
+
- Authentication: \`Authorization: Token token=<API_KEY>\` header (note: this is NOT HTTP Basic Auth like Freshdesk/Freshservice).
|
|
589
|
+
- Pagination: 1-indexed \`page\` + \`per_page\` (default 25, max 100). View list responses include \`meta: { total_pages, total }\`.
|
|
590
|
+
- Includes: most list/get endpoints accept \`?include=\` with a comma-separated list of related resources to inline.
|
|
591
|
+
|
|
592
|
+
#### Resource Endpoints
|
|
593
|
+
|
|
594
|
+
**Contacts**
|
|
595
|
+
- GET \`/contacts/filters\` \u2014 List saved views (a.k.a. filters)
|
|
596
|
+
- GET \`/contacts/view/{view_id}\` \u2014 Contacts in a view (paginated)
|
|
597
|
+
- GET \`/contacts/{id}\` \u2014 Get a contact (use \`?include=owner,creator,deals,sales_accounts\`)
|
|
598
|
+
- POST \`/contacts\` \u2014 Create a contact (body: \`{ "contact": { ... } }\`)
|
|
599
|
+
- PUT \`/contacts/{id}\` \u2014 Update a contact
|
|
600
|
+
|
|
601
|
+
**Sales accounts (companies)**
|
|
602
|
+
- GET \`/sales_accounts/filters\` \u2014 List saved views
|
|
603
|
+
- GET \`/sales_accounts/view/{view_id}\` \u2014 Accounts in a view
|
|
604
|
+
- GET \`/sales_accounts/{id}\` \u2014 Get an account
|
|
605
|
+
- POST \`/sales_accounts\` / PUT \`/sales_accounts/{id}\` \u2014 Write
|
|
606
|
+
|
|
607
|
+
**Deals**
|
|
608
|
+
- GET \`/deals/filters\` \u2014 List saved views
|
|
609
|
+
- GET \`/deals/view/{view_id}\` \u2014 Deals in a view
|
|
610
|
+
- GET \`/deals/{id}\` \u2014 Get a deal (use \`?include=owner,deal_stage,sales_account,contacts\`)
|
|
611
|
+
- POST \`/deals\` / PUT \`/deals/{id}\` \u2014 Write
|
|
612
|
+
|
|
613
|
+
**Leads (Sales Cloud only)**
|
|
614
|
+
- GET \`/leads/filters\` \u2014 Saved views
|
|
615
|
+
- GET \`/leads/view/{view_id}\` \u2014 Leads in a view
|
|
616
|
+
- GET \`/leads/{id}\` \u2014 Get a lead
|
|
617
|
+
|
|
618
|
+
**Sales activities & tasks**
|
|
619
|
+
- GET \`/sales_activities\` \u2014 List sales activities
|
|
620
|
+
- GET \`/tasks\` \u2014 List tasks
|
|
621
|
+
- GET \`/appointments\` \u2014 List appointments
|
|
622
|
+
- POST \`/notes\` \u2014 Create a note (body references a target contact/account/deal)
|
|
623
|
+
|
|
624
|
+
**Search & lookup**
|
|
625
|
+
- GET \`/lookup\` \u2014 Lookup a record by attribute. Query params: \`q\` (value), \`f\` (field \u2014 typically \`email\`/\`mobile_number\`), \`entities\` (\`contact\`/\`sales_account\`/\`deal\`/\`lead\`).
|
|
626
|
+
|
|
627
|
+
**Selector metadata**
|
|
628
|
+
- GET \`/selector/owners\` \u2014 Sales reps available as owners
|
|
629
|
+
- GET \`/selector/deal_pipelines\` \u2014 Pipelines
|
|
630
|
+
- GET \`/selector/deal_stages\` \u2014 Stages
|
|
631
|
+
- GET \`/selector/deal_types\` \u2014 Deal types
|
|
632
|
+
- GET \`/selector/lifecycle_stages\` \u2014 Lifecycle stages
|
|
633
|
+
- GET \`/selector/territories\` / \`/selector/business_types\` / \`/selector/industry_types\` / \`/selector/contact_statuses\` / \`/selector/sales_activity_types\``,
|
|
634
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
635
|
+
|
|
636
|
+
- \`freshsales_request\`: Freshsales / Freshworks CRM REST API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u9023\u7D61\u5148\u3001\u53D6\u5F15\u5148\u3001\u5546\u8AC7\u3001\u30EA\u30FC\u30C9\u3001\u55B6\u696D\u6D3B\u52D5\u3001\u5404\u7A2E\u30BB\u30EC\u30AF\u30BF\u30FC\u30E1\u30BF\u60C5\u5831\u306E\u53D6\u5F97\u30FB\u66F4\u65B0\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08\`Authorization: Token token=<API_KEY>\` \u30D8\u30C3\u30C0\u30FC \u2014 Freshdesk/Freshservice \u3068\u7570\u306A\u308A HTTP Basic Auth \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF09\u3068\u30D9\u30FC\u30B9URL\uFF08https://<bundle>.myfreshworks.com/crm/sales/api\uFF09\u306F\u81EA\u52D5\u3067\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
|
|
637
|
+
|
|
638
|
+
### Business Logic
|
|
639
|
+
|
|
640
|
+
\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u30B3\u30CD\u30AF\u30BFSDK\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u8A8D\u8A3C\u60C5\u5831\u3092\u8AAD\u307F\u53D6\u3089\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
|
|
641
|
+
|
|
642
|
+
SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
|
|
643
|
+
- \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304Dfetch
|
|
644
|
+
- \`client.listContactViews()\` / \`client.listContactsInView(viewId, options?)\` \u2014 \u4FDD\u5B58\u30D3\u30E5\u30FC\u7D4C\u7531\u3067\u9023\u7D61\u5148\u4E00\u89A7\u3092\u53D6\u5F97
|
|
645
|
+
- \`client.getContact(contactId, options?)\` \u2014 \u5358\u4E00\u9023\u7D61\u5148\u306E\u53D6\u5F97
|
|
646
|
+
- \`client.createContact(contactData)\` / \`client.updateContact(contactId, contactData)\` \u2014 \u9023\u7D61\u5148\u306E\u4F5C\u6210\u30FB\u66F4\u65B0
|
|
647
|
+
- \`client.listAccountViews()\` / \`client.listAccountsInView(viewId, options?)\` \u2014 \u53D6\u5F15\u5148\uFF08sales_accounts\uFF09
|
|
648
|
+
- \`client.listDealViews()\` / \`client.listDealsInView(viewId, options?)\` / \`client.getDeal(dealId, options?)\` \u2014 \u5546\u8AC7
|
|
649
|
+
- \`client.lookup({ q, f, entities })\` \u2014 \u5C5E\u6027\u3067\u30EC\u30B3\u30FC\u30C9\u691C\u7D22\uFF08\u4F8B: \u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3067\u9023\u7D61\u5148\u3092\u691C\u7D22\uFF09
|
|
650
|
+
|
|
651
|
+
\`\`\`ts
|
|
652
|
+
import type { Context } from "hono";
|
|
653
|
+
import { connection } from "@squadbase/vite-server/connectors/freshsales";
|
|
654
|
+
|
|
655
|
+
const fs = connection("<connectionId>");
|
|
656
|
+
|
|
657
|
+
export default async function handler(c: Context) {
|
|
658
|
+
const views = await fs.listDealViews();
|
|
659
|
+
const firstViewId = (views.filters[0] as { id: number }).id;
|
|
660
|
+
const { deals, meta } = await fs.listDealsInView(firstViewId, {
|
|
661
|
+
per_page: 100,
|
|
662
|
+
include: "owner,deal_stage,sales_account",
|
|
663
|
+
});
|
|
664
|
+
return c.json({ deals, totalPages: meta.total_pages });
|
|
665
|
+
}
|
|
666
|
+
\`\`\`
|
|
667
|
+
|
|
668
|
+
### Freshsales / Freshworks CRM REST API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
669
|
+
|
|
670
|
+
- \u30D9\u30FC\u30B9URL: \`https://<bundle>.myfreshworks.com/crm/sales/api\`
|
|
671
|
+
- \u8A8D\u8A3C: \`Authorization: Token token=<API_KEY>\` \u30D8\u30C3\u30C0\u30FC\uFF08Freshdesk/Freshservice \u3068\u7570\u306A\u308A HTTP Basic Auth \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF09\u3002
|
|
672
|
+
- \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3: 1\u59CB\u307E\u308A\u306E \`page\` \u3068 \`per_page\`\uFF08\u30C7\u30D5\u30A9\u30EB\u30C825\u3001\u6700\u5927100\uFF09\u3002\u30D3\u30E5\u30FC\u4E00\u89A7\u7CFB\u306E\u30EC\u30B9\u30DD\u30F3\u30B9\u306B\u306F \`meta: { total_pages, total }\` \u304C\u542B\u307E\u308C\u307E\u3059\u3002
|
|
673
|
+
- \u30A4\u30F3\u30AF\u30EB\u30FC\u30C9: \u307B\u3068\u3093\u3069\u306E\u4E00\u89A7/\u53D6\u5F97\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3067 \`?include=\` \u306B\u30AB\u30F3\u30DE\u533A\u5207\u308A\u3067\u95A2\u9023\u30EA\u30BD\u30FC\u30B9\u3092\u6307\u5B9A\u3057\u3066\u30A4\u30F3\u30E9\u30A4\u30F3\u5C55\u958B\u3067\u304D\u307E\u3059\u3002
|
|
674
|
+
|
|
675
|
+
#### \u30EA\u30BD\u30FC\u30B9\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
|
|
676
|
+
|
|
677
|
+
**\u9023\u7D61\u5148\uFF08Contacts\uFF09**
|
|
678
|
+
- GET \`/contacts/filters\` \u2014 \u4FDD\u5B58\u30D3\u30E5\u30FC\u4E00\u89A7\uFF08filters\uFF09
|
|
679
|
+
- GET \`/contacts/view/{view_id}\` \u2014 \u30D3\u30E5\u30FC\u5185\u306E\u9023\u7D61\u5148\u4E00\u89A7\uFF08\u30DA\u30FC\u30B8\u30F3\u30B0\uFF09
|
|
680
|
+
- GET \`/contacts/{id}\` \u2014 \u9023\u7D61\u5148\u306E\u53D6\u5F97\uFF08\`?include=owner,creator,deals,sales_accounts\` \u3067\u95A2\u9023\u5C55\u958B\uFF09
|
|
681
|
+
- POST \`/contacts\` \u2014 \u9023\u7D61\u5148\u306E\u4F5C\u6210\uFF08body: \`{ "contact": { ... } }\`\uFF09
|
|
682
|
+
- PUT \`/contacts/{id}\` \u2014 \u9023\u7D61\u5148\u306E\u66F4\u65B0
|
|
683
|
+
|
|
684
|
+
**\u53D6\u5F15\u5148\uFF08Sales accounts / \u4F1A\u793E\uFF09**
|
|
685
|
+
- GET \`/sales_accounts/filters\` \u2014 \u4FDD\u5B58\u30D3\u30E5\u30FC\u4E00\u89A7
|
|
686
|
+
- GET \`/sales_accounts/view/{view_id}\` \u2014 \u30D3\u30E5\u30FC\u5185\u306E\u53D6\u5F15\u5148
|
|
687
|
+
- GET \`/sales_accounts/{id}\` \u2014 \u53D6\u5F15\u5148\u306E\u53D6\u5F97
|
|
688
|
+
- POST \`/sales_accounts\` / PUT \`/sales_accounts/{id}\` \u2014 \u4F5C\u6210\u30FB\u66F4\u65B0
|
|
689
|
+
|
|
690
|
+
**\u5546\u8AC7\uFF08Deals\uFF09**
|
|
691
|
+
- GET \`/deals/filters\` \u2014 \u4FDD\u5B58\u30D3\u30E5\u30FC\u4E00\u89A7
|
|
692
|
+
- GET \`/deals/view/{view_id}\` \u2014 \u30D3\u30E5\u30FC\u5185\u306E\u5546\u8AC7
|
|
693
|
+
- GET \`/deals/{id}\` \u2014 \u5546\u8AC7\u306E\u53D6\u5F97\uFF08\`?include=owner,deal_stage,sales_account,contacts\` \u3067\u95A2\u9023\u5C55\u958B\uFF09
|
|
694
|
+
- POST \`/deals\` / PUT \`/deals/{id}\` \u2014 \u4F5C\u6210\u30FB\u66F4\u65B0
|
|
695
|
+
|
|
696
|
+
**\u30EA\u30FC\u30C9\uFF08Leads \u2014 Sales Cloud \u306E\u307F\uFF09**
|
|
697
|
+
- GET \`/leads/filters\` \u2014 \u4FDD\u5B58\u30D3\u30E5\u30FC\u4E00\u89A7
|
|
698
|
+
- GET \`/leads/view/{view_id}\` \u2014 \u30D3\u30E5\u30FC\u5185\u306E\u30EA\u30FC\u30C9
|
|
699
|
+
- GET \`/leads/{id}\` \u2014 \u30EA\u30FC\u30C9\u306E\u53D6\u5F97
|
|
700
|
+
|
|
701
|
+
**\u55B6\u696D\u6D3B\u52D5 & \u30BF\u30B9\u30AF**
|
|
702
|
+
- GET \`/sales_activities\` \u2014 \u55B6\u696D\u6D3B\u52D5\u306E\u4E00\u89A7
|
|
703
|
+
- GET \`/tasks\` \u2014 \u30BF\u30B9\u30AF\u4E00\u89A7
|
|
704
|
+
- GET \`/appointments\` \u2014 \u30A2\u30DD\u30A4\u30F3\u30C8\u30E1\u30F3\u30C8\u4E00\u89A7
|
|
705
|
+
- POST \`/notes\` \u2014 \u30CE\u30FC\u30C8\u306E\u4F5C\u6210\uFF08body \u3067\u9023\u7D61\u5148/\u53D6\u5F15\u5148/\u5546\u8AC7\u3092\u53C2\u7167\uFF09
|
|
706
|
+
|
|
707
|
+
**\u691C\u7D22 & \u30EB\u30C3\u30AF\u30A2\u30C3\u30D7**
|
|
708
|
+
- GET \`/lookup\` \u2014 \u5C5E\u6027\u306B\u3088\u308B\u30EC\u30B3\u30FC\u30C9\u691C\u7D22\u3002\u30AF\u30A8\u30EA: \`q\`\uFF08\u5024\uFF09\u3001\`f\`\uFF08\u30D5\u30A3\u30FC\u30EB\u30C9 \u2014 \u901A\u5E38 \`email\`/\`mobile_number\`\uFF09\u3001\`entities\`\uFF08\`contact\`/\`sales_account\`/\`deal\`/\`lead\`\uFF09\u3002
|
|
709
|
+
|
|
710
|
+
**\u30BB\u30EC\u30AF\u30BF\u30FC\u30E1\u30BF\u60C5\u5831**
|
|
711
|
+
- GET \`/selector/owners\` \u2014 \u30AA\u30FC\u30CA\u30FC\uFF08\u55B6\u696D\u62C5\u5F53\uFF09\u4E00\u89A7
|
|
712
|
+
- GET \`/selector/deal_pipelines\` \u2014 \u30D1\u30A4\u30D7\u30E9\u30A4\u30F3
|
|
713
|
+
- GET \`/selector/deal_stages\` \u2014 \u30B9\u30C6\u30FC\u30B8
|
|
714
|
+
- GET \`/selector/deal_types\` \u2014 \u5546\u8AC7\u30BF\u30A4\u30D7
|
|
715
|
+
- GET \`/selector/lifecycle_stages\` \u2014 \u30E9\u30A4\u30D5\u30B5\u30A4\u30AF\u30EB\u30B9\u30C6\u30FC\u30B8
|
|
716
|
+
- GET \`/selector/territories\` / \`/selector/business_types\` / \`/selector/industry_types\` / \`/selector/contact_statuses\` / \`/selector/sales_activity_types\``
|
|
717
|
+
},
|
|
718
|
+
tools
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// src/connectors/create-connector-sdk.ts
|
|
722
|
+
import { readFileSync } from "fs";
|
|
723
|
+
import path from "path";
|
|
724
|
+
|
|
725
|
+
// src/connector-client/env.ts
|
|
726
|
+
function resolveEnvVar(entry, key, connectionId) {
|
|
727
|
+
const envVarName = entry.envVars[key];
|
|
728
|
+
if (!envVarName) {
|
|
729
|
+
throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
|
|
730
|
+
}
|
|
731
|
+
const value = process.env[envVarName];
|
|
732
|
+
if (!value) {
|
|
733
|
+
throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
|
|
734
|
+
}
|
|
735
|
+
return value;
|
|
736
|
+
}
|
|
737
|
+
function resolveEnvVarOptional(entry, key) {
|
|
738
|
+
const envVarName = entry.envVars[key];
|
|
739
|
+
if (!envVarName) return void 0;
|
|
740
|
+
return process.env[envVarName] || void 0;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/connector-client/proxy-fetch.ts
|
|
744
|
+
import { getContext } from "hono/context-storage";
|
|
745
|
+
import { getCookie } from "hono/cookie";
|
|
746
|
+
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
747
|
+
function normalizeHeaders(input) {
|
|
748
|
+
const out = {};
|
|
749
|
+
if (!input) return out;
|
|
750
|
+
new Headers(input).forEach((value, key) => {
|
|
751
|
+
out[key] = value;
|
|
752
|
+
});
|
|
753
|
+
return out;
|
|
754
|
+
}
|
|
755
|
+
function createSandboxProxyFetch(connectionId) {
|
|
756
|
+
return async (input, init) => {
|
|
757
|
+
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
758
|
+
const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
|
|
759
|
+
if (!token || !sandboxId) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
765
|
+
const originalMethod = init?.method ?? "GET";
|
|
766
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
767
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
768
|
+
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
769
|
+
return fetch(proxyUrl, {
|
|
770
|
+
method: "POST",
|
|
771
|
+
headers: {
|
|
772
|
+
"Content-Type": "application/json",
|
|
773
|
+
Authorization: `Bearer ${token}`
|
|
774
|
+
},
|
|
775
|
+
body: JSON.stringify({
|
|
776
|
+
url: originalUrl,
|
|
777
|
+
method: originalMethod,
|
|
778
|
+
headers: normalizeHeaders(init?.headers),
|
|
779
|
+
body: originalBody
|
|
780
|
+
})
|
|
781
|
+
});
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function createDeployedAppProxyFetch(connectionId) {
|
|
785
|
+
const projectId = process.env["SQUADBASE_PROJECT_ID"];
|
|
786
|
+
if (!projectId) {
|
|
787
|
+
throw new Error(
|
|
788
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
792
|
+
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
793
|
+
return async (input, init) => {
|
|
794
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
795
|
+
const originalMethod = init?.method ?? "GET";
|
|
796
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
797
|
+
const c = getContext();
|
|
798
|
+
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
799
|
+
if (!appSession) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
"No authentication method available for connection proxy."
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
return fetch(proxyUrl, {
|
|
805
|
+
method: "POST",
|
|
806
|
+
headers: {
|
|
807
|
+
"Content-Type": "application/json",
|
|
808
|
+
Authorization: `Bearer ${appSession}`
|
|
809
|
+
},
|
|
810
|
+
body: JSON.stringify({
|
|
811
|
+
url: originalUrl,
|
|
812
|
+
method: originalMethod,
|
|
813
|
+
headers: normalizeHeaders(init?.headers),
|
|
814
|
+
body: originalBody
|
|
815
|
+
})
|
|
816
|
+
});
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
function createProxyFetch(connectionId) {
|
|
820
|
+
if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
|
|
821
|
+
return createSandboxProxyFetch(connectionId);
|
|
822
|
+
}
|
|
823
|
+
return createDeployedAppProxyFetch(connectionId);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/connectors/create-connector-sdk.ts
|
|
827
|
+
function loadConnectionsSync() {
|
|
828
|
+
const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
|
|
829
|
+
try {
|
|
830
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
831
|
+
return JSON.parse(raw);
|
|
832
|
+
} catch {
|
|
833
|
+
return {};
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
function createConnectorSdk(plugin, createClient2) {
|
|
837
|
+
return (connectionId) => {
|
|
838
|
+
const connections = loadConnectionsSync();
|
|
839
|
+
const entry = connections[connectionId];
|
|
840
|
+
if (!entry) {
|
|
841
|
+
throw new Error(
|
|
842
|
+
`Connection "${connectionId}" not found in .squadbase/connections.json`
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
if (entry.connector.slug !== plugin.slug) {
|
|
846
|
+
throw new Error(
|
|
847
|
+
`Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
const params = {};
|
|
851
|
+
for (const param of Object.values(plugin.parameters)) {
|
|
852
|
+
if (param.required) {
|
|
853
|
+
params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
|
|
854
|
+
} else {
|
|
855
|
+
const val = resolveEnvVarOptional(entry, param.slug);
|
|
856
|
+
if (val !== void 0) params[param.slug] = val;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return createClient2(params, createProxyFetch(connectionId));
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// src/connectors/entries/freshsales.ts
|
|
864
|
+
var connection = createConnectorSdk(freshsalesConnector, createClient);
|
|
865
|
+
export {
|
|
866
|
+
connection
|
|
867
|
+
};
|