@squadbase/vite-server 0.1.5-dev.0 → 0.1.6
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 +411 -236
- package/dist/connectors/airtable-oauth.js +0 -8
- package/dist/connectors/gmail-oauth.js +0 -8
- package/dist/connectors/gmail.js +0 -8
- package/dist/connectors/google-ads.js +0 -8
- package/dist/connectors/google-analytics-oauth.js +0 -8
- package/dist/connectors/google-calendar-oauth.js +0 -8
- package/dist/connectors/google-calendar.js +0 -10
- package/dist/connectors/hubspot-oauth.js +0 -6
- package/dist/connectors/influxdb.d.ts +5 -0
- package/dist/connectors/influxdb.js +767 -0
- package/dist/connectors/intercom-oauth.js +0 -6
- package/dist/connectors/linear.d.ts +5 -0
- package/dist/connectors/linear.js +688 -0
- package/dist/connectors/linkedin-ads.js +0 -8
- package/dist/connectors/meta-ads-oauth.d.ts +5 -0
- package/dist/connectors/meta-ads-oauth.js +795 -0
- package/dist/connectors/meta-ads.d.ts +5 -0
- package/dist/connectors/meta-ads.js +780 -0
- package/dist/connectors/notion-oauth.js +0 -6
- package/dist/connectors/shopify-oauth.js +0 -6
- package/dist/connectors/stripe-api-key.js +0 -4
- package/dist/connectors/stripe-oauth.js +0 -6
- package/dist/connectors/tiktok-ads.d.ts +5 -0
- package/dist/connectors/tiktok-ads.js +840 -0
- package/dist/connectors/zendesk-oauth.js +0 -6
- package/dist/index.js +411 -236
- package/dist/main.js +411 -236
- package/dist/vite-plugin.js +411 -236
- package/package.json +21 -1
|
@@ -0,0 +1,688 @@
|
|
|
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/linear/parameters.ts
|
|
46
|
+
var parameters = {
|
|
47
|
+
apiKey: new ParameterDefinition({
|
|
48
|
+
slug: "api-key",
|
|
49
|
+
name: "Linear API Key",
|
|
50
|
+
description: "Personal API key generated from Linear Settings > Security & access > API.",
|
|
51
|
+
envVarBaseKey: "LINEAR_API_KEY",
|
|
52
|
+
type: "text",
|
|
53
|
+
secret: true,
|
|
54
|
+
required: true
|
|
55
|
+
})
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// ../connectors/src/connectors/linear/sdk/index.ts
|
|
59
|
+
var BASE_URL = "https://api.linear.app/graphql";
|
|
60
|
+
function createClient(params) {
|
|
61
|
+
const apiKey = params[parameters.apiKey.slug];
|
|
62
|
+
if (!apiKey) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`linear: missing required parameter: ${parameters.apiKey.slug}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
function authHeaders(extra) {
|
|
68
|
+
const headers = new Headers(extra);
|
|
69
|
+
headers.set("Authorization", apiKey);
|
|
70
|
+
headers.set("Content-Type", "application/json");
|
|
71
|
+
return headers;
|
|
72
|
+
}
|
|
73
|
+
async function gql(query, variables) {
|
|
74
|
+
const body = { query };
|
|
75
|
+
if (variables) body.variables = variables;
|
|
76
|
+
const res = await fetch(BASE_URL, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: authHeaders(),
|
|
79
|
+
body: JSON.stringify(body)
|
|
80
|
+
});
|
|
81
|
+
const json = await res.json();
|
|
82
|
+
if (json.errors && json.errors.length > 0) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`linear graphql: ${json.errors.map((e) => e.message).join("; ")}`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (!json.data) {
|
|
88
|
+
throw new Error("linear graphql: no data in response");
|
|
89
|
+
}
|
|
90
|
+
return json.data;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
graphql: gql,
|
|
94
|
+
request(init) {
|
|
95
|
+
const headers = new Headers(init?.headers);
|
|
96
|
+
headers.set("Authorization", apiKey);
|
|
97
|
+
headers.set("Content-Type", "application/json");
|
|
98
|
+
return fetch(BASE_URL, { ...init, headers });
|
|
99
|
+
},
|
|
100
|
+
async listTeams(options) {
|
|
101
|
+
const vars = {};
|
|
102
|
+
if (options?.first) vars.first = options.first;
|
|
103
|
+
if (options?.after) vars.after = options.after;
|
|
104
|
+
if (options?.includeArchived) vars.includeArchived = options.includeArchived;
|
|
105
|
+
const args = Object.entries(vars).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ");
|
|
106
|
+
const argsStr = args ? `(${args})` : "";
|
|
107
|
+
const result = await gql(`{ teams${argsStr} { nodes { id name key description } pageInfo { hasNextPage endCursor } } }`);
|
|
108
|
+
return result.teams;
|
|
109
|
+
},
|
|
110
|
+
async listIssues(options) {
|
|
111
|
+
const vars = {};
|
|
112
|
+
if (options?.first) vars.first = options.first;
|
|
113
|
+
if (options?.after) vars.after = options.after;
|
|
114
|
+
if (options?.includeArchived) vars.includeArchived = options.includeArchived;
|
|
115
|
+
if (options?.orderBy) vars.orderBy = options.orderBy;
|
|
116
|
+
let filterStr = "";
|
|
117
|
+
if (options?.teamId) {
|
|
118
|
+
filterStr = `, filter: { team: { id: { eq: "${options.teamId}" } } }`;
|
|
119
|
+
}
|
|
120
|
+
const args = Object.entries(vars).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ");
|
|
121
|
+
const argsStr = args || filterStr ? `(${args}${filterStr})` : "";
|
|
122
|
+
const result = await gql(`{ issues${argsStr} { nodes { id identifier title description state { id name } assignee { id name } priority priorityLabel labels { nodes { id name } } project { id name } cycle { id name } createdAt updatedAt } pageInfo { hasNextPage endCursor } } }`);
|
|
123
|
+
return result.issues;
|
|
124
|
+
},
|
|
125
|
+
async getIssue(id) {
|
|
126
|
+
const result = await gql(
|
|
127
|
+
`{ issue(id: "${id}") { id identifier title description state { id name } assignee { id name } priority priorityLabel labels { nodes { id name } } project { id name } cycle { id name } team { id name } createdAt updatedAt completedAt } }`
|
|
128
|
+
);
|
|
129
|
+
return result.issue;
|
|
130
|
+
},
|
|
131
|
+
async createIssue(input) {
|
|
132
|
+
const result = await gql(
|
|
133
|
+
`mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title state { id name } } } }`,
|
|
134
|
+
{ input }
|
|
135
|
+
);
|
|
136
|
+
return result.issueCreate;
|
|
137
|
+
},
|
|
138
|
+
async listProjects(options) {
|
|
139
|
+
const vars = {};
|
|
140
|
+
if (options?.first) vars.first = options.first;
|
|
141
|
+
if (options?.after) vars.after = options.after;
|
|
142
|
+
if (options?.includeArchived) vars.includeArchived = options.includeArchived;
|
|
143
|
+
const args = Object.entries(vars).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ");
|
|
144
|
+
const argsStr = args ? `(${args})` : "";
|
|
145
|
+
const result = await gql(`{ projects${argsStr} { nodes { id name description state startDate targetDate } pageInfo { hasNextPage endCursor } } }`);
|
|
146
|
+
return result.projects;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ../connectors/src/connector-onboarding.ts
|
|
152
|
+
var ConnectorOnboarding = class {
|
|
153
|
+
/** Phase 1: Connection setup instructions (optional — some connectors don't need this) */
|
|
154
|
+
connectionSetupInstructions;
|
|
155
|
+
/** Phase 2: Data overview instructions */
|
|
156
|
+
dataOverviewInstructions;
|
|
157
|
+
constructor(config) {
|
|
158
|
+
this.connectionSetupInstructions = config.connectionSetupInstructions;
|
|
159
|
+
this.dataOverviewInstructions = config.dataOverviewInstructions;
|
|
160
|
+
}
|
|
161
|
+
getConnectionSetupPrompt(language) {
|
|
162
|
+
return this.connectionSetupInstructions?.[language] ?? null;
|
|
163
|
+
}
|
|
164
|
+
getDataOverviewInstructions(language) {
|
|
165
|
+
return this.dataOverviewInstructions[language];
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// ../connectors/src/connector-tool.ts
|
|
170
|
+
var ConnectorTool = class {
|
|
171
|
+
name;
|
|
172
|
+
description;
|
|
173
|
+
inputSchema;
|
|
174
|
+
outputSchema;
|
|
175
|
+
_execute;
|
|
176
|
+
constructor(config) {
|
|
177
|
+
this.name = config.name;
|
|
178
|
+
this.description = config.description;
|
|
179
|
+
this.inputSchema = config.inputSchema;
|
|
180
|
+
this.outputSchema = config.outputSchema;
|
|
181
|
+
this._execute = config.execute;
|
|
182
|
+
}
|
|
183
|
+
createTool(connections, config) {
|
|
184
|
+
return {
|
|
185
|
+
description: this.description,
|
|
186
|
+
inputSchema: this.inputSchema,
|
|
187
|
+
outputSchema: this.outputSchema,
|
|
188
|
+
execute: (input) => this._execute(input, connections, config)
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// ../connectors/src/connector-plugin.ts
|
|
194
|
+
var ConnectorPlugin = class _ConnectorPlugin {
|
|
195
|
+
slug;
|
|
196
|
+
authType;
|
|
197
|
+
name;
|
|
198
|
+
description;
|
|
199
|
+
iconUrl;
|
|
200
|
+
parameters;
|
|
201
|
+
releaseFlag;
|
|
202
|
+
proxyPolicy;
|
|
203
|
+
experimentalAttributes;
|
|
204
|
+
onboarding;
|
|
205
|
+
systemPrompt;
|
|
206
|
+
tools;
|
|
207
|
+
query;
|
|
208
|
+
checkConnection;
|
|
209
|
+
constructor(config) {
|
|
210
|
+
this.slug = config.slug;
|
|
211
|
+
this.authType = config.authType;
|
|
212
|
+
this.name = config.name;
|
|
213
|
+
this.description = config.description;
|
|
214
|
+
this.iconUrl = config.iconUrl;
|
|
215
|
+
this.parameters = config.parameters;
|
|
216
|
+
this.releaseFlag = config.releaseFlag;
|
|
217
|
+
this.proxyPolicy = config.proxyPolicy;
|
|
218
|
+
this.experimentalAttributes = config.experimentalAttributes;
|
|
219
|
+
this.onboarding = config.onboarding;
|
|
220
|
+
this.systemPrompt = config.systemPrompt;
|
|
221
|
+
this.tools = config.tools;
|
|
222
|
+
this.query = config.query;
|
|
223
|
+
this.checkConnection = config.checkConnection;
|
|
224
|
+
}
|
|
225
|
+
get connectorKey() {
|
|
226
|
+
return _ConnectorPlugin.deriveKey(this.slug, this.authType);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Create tools for connections that belong to this connector.
|
|
230
|
+
* Filters connections by connectorKey internally.
|
|
231
|
+
* Returns tools keyed as `${connectorKey}_${toolName}`.
|
|
232
|
+
*/
|
|
233
|
+
createTools(connections, config, opts) {
|
|
234
|
+
const myConnections = connections.filter(
|
|
235
|
+
(c) => _ConnectorPlugin.deriveKey(c.connector.slug, c.connector.authType) === this.connectorKey
|
|
236
|
+
);
|
|
237
|
+
const result = {};
|
|
238
|
+
for (const t of Object.values(this.tools)) {
|
|
239
|
+
const tool = t.createTool(myConnections, config);
|
|
240
|
+
const originalToModelOutput = tool.toModelOutput;
|
|
241
|
+
result[`${this.connectorKey}_${t.name}`] = {
|
|
242
|
+
...tool,
|
|
243
|
+
toModelOutput: async (options) => {
|
|
244
|
+
if (!originalToModelOutput) {
|
|
245
|
+
return opts.truncateOutput(options.output);
|
|
246
|
+
}
|
|
247
|
+
const modelOutput = await originalToModelOutput(options);
|
|
248
|
+
if (modelOutput.type === "text" || modelOutput.type === "json") {
|
|
249
|
+
return opts.truncateOutput(modelOutput.value);
|
|
250
|
+
}
|
|
251
|
+
return modelOutput;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
static deriveKey(slug, authType) {
|
|
258
|
+
if (authType) return `${slug}-${authType}`;
|
|
259
|
+
const LEGACY_NULL_AUTH_TYPE_MAP = {
|
|
260
|
+
// user-password
|
|
261
|
+
"postgresql": "user-password",
|
|
262
|
+
"mysql": "user-password",
|
|
263
|
+
"clickhouse": "user-password",
|
|
264
|
+
"kintone": "user-password",
|
|
265
|
+
"squadbase-db": "user-password",
|
|
266
|
+
// service-account
|
|
267
|
+
"snowflake": "service-account",
|
|
268
|
+
"bigquery": "service-account",
|
|
269
|
+
"google-analytics": "service-account",
|
|
270
|
+
"google-calendar": "service-account",
|
|
271
|
+
"aws-athena": "service-account",
|
|
272
|
+
"redshift": "service-account",
|
|
273
|
+
// api-key
|
|
274
|
+
"databricks": "api-key",
|
|
275
|
+
"dbt": "api-key",
|
|
276
|
+
"airtable": "api-key",
|
|
277
|
+
"openai": "api-key",
|
|
278
|
+
"gemini": "api-key",
|
|
279
|
+
"anthropic": "api-key",
|
|
280
|
+
"wix-store": "api-key"
|
|
281
|
+
};
|
|
282
|
+
const fallbackAuthType = LEGACY_NULL_AUTH_TYPE_MAP[slug];
|
|
283
|
+
if (fallbackAuthType) return `${slug}-${fallbackAuthType}`;
|
|
284
|
+
return slug;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// ../connectors/src/auth-types.ts
|
|
289
|
+
var AUTH_TYPES = {
|
|
290
|
+
OAUTH: "oauth",
|
|
291
|
+
API_KEY: "api-key",
|
|
292
|
+
JWT: "jwt",
|
|
293
|
+
SERVICE_ACCOUNT: "service-account",
|
|
294
|
+
PAT: "pat",
|
|
295
|
+
USER_PASSWORD: "user-password"
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// ../connectors/src/connectors/linear/setup.ts
|
|
299
|
+
var linearOnboarding = new ConnectorOnboarding({
|
|
300
|
+
dataOverviewInstructions: {
|
|
301
|
+
en: `1. Call linear_request with query \`{ teams { nodes { id name } } }\` to list all teams
|
|
302
|
+
2. Call linear_request with query \`{ workflowStates { nodes { id name type } } }\` to list workflow states
|
|
303
|
+
3. Call linear_request with query \`{ issues(first: 5, orderBy: updatedAt) { nodes { id identifier title state { name } assignee { name } priority } } }\` to sample recent issues
|
|
304
|
+
4. Call linear_request with query \`{ projects(first: 5) { nodes { id name state } } }\` to list projects
|
|
305
|
+
5. Explore other resources (cycles, labels, users) as needed`,
|
|
306
|
+
ja: `1. linear_request \u3067\u30AF\u30A8\u30EA \`{ teams { nodes { id name } } }\` \u3092\u547C\u3073\u51FA\u3057\u3001\u30C1\u30FC\u30E0\u4E00\u89A7\u3092\u53D6\u5F97
|
|
307
|
+
2. linear_request \u3067\u30AF\u30A8\u30EA \`{ workflowStates { nodes { id name type } } }\` \u3092\u547C\u3073\u51FA\u3057\u3001\u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u72B6\u614B\u4E00\u89A7\u3092\u53D6\u5F97
|
|
308
|
+
3. linear_request \u3067\u30AF\u30A8\u30EA \`{ issues(first: 5, orderBy: updatedAt) { nodes { id identifier title state { name } assignee { name } priority } } }\` \u3092\u547C\u3073\u51FA\u3057\u3001\u6700\u8FD1\u306EIssue\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0
|
|
309
|
+
4. linear_request \u3067\u30AF\u30A8\u30EA \`{ projects(first: 5) { nodes { id name state } } }\` \u3092\u547C\u3073\u51FA\u3057\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97
|
|
310
|
+
5. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u4ED6\u306E\u30EA\u30BD\u30FC\u30B9\uFF08cycles\u3001labels\u3001users\uFF09\u3092\u63A2\u7D22`
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// ../connectors/src/connectors/linear/tools/request.ts
|
|
315
|
+
import { z } from "zod";
|
|
316
|
+
var BASE_URL2 = "https://api.linear.app/graphql";
|
|
317
|
+
var REQUEST_TIMEOUT_MS = 6e4;
|
|
318
|
+
var inputSchema = z.object({
|
|
319
|
+
toolUseIntent: z.string().optional().describe(
|
|
320
|
+
"Brief description of what you intend to accomplish with this tool call"
|
|
321
|
+
),
|
|
322
|
+
connectionId: z.string().describe("ID of the Linear connection to use"),
|
|
323
|
+
query: z.string().describe(
|
|
324
|
+
`GraphQL query or mutation string. Use standard GraphQL syntax. Example: '{ issues(first: 10) { nodes { id title state { name } } } }' for queries, or 'mutation { issueCreate(input: { title: "Bug fix", teamId: "TEAM_ID" }) { success issue { id title } } }' for mutations.`
|
|
325
|
+
),
|
|
326
|
+
variables: z.record(z.string(), z.unknown()).optional().describe(
|
|
327
|
+
'Optional GraphQL variables object. Use with parameterized queries, e.g. { "teamId": "abc-123", "first": 10 }'
|
|
328
|
+
)
|
|
329
|
+
});
|
|
330
|
+
var outputSchema = z.discriminatedUnion("success", [
|
|
331
|
+
z.object({
|
|
332
|
+
success: z.literal(true),
|
|
333
|
+
data: z.record(z.string(), z.unknown())
|
|
334
|
+
}),
|
|
335
|
+
z.object({
|
|
336
|
+
success: z.literal(false),
|
|
337
|
+
error: z.string()
|
|
338
|
+
})
|
|
339
|
+
]);
|
|
340
|
+
var requestTool = new ConnectorTool({
|
|
341
|
+
name: "request",
|
|
342
|
+
description: `Send authenticated GraphQL queries and mutations to the Linear API (https://api.linear.app/graphql).
|
|
343
|
+
Use this tool for all Linear interactions: querying issues, projects, teams, cycles, users, labels, workflow states, and performing mutations like creating/updating issues and comments.
|
|
344
|
+
Linear's API is GraphQL-only \u2014 there is no REST API. All requests are POST with a JSON body containing "query" and optional "variables".
|
|
345
|
+
Archived resources are hidden by default; pass includeArchived: true in query arguments to include them. Pagination uses Relay-style cursors with first/after and last/before arguments.`,
|
|
346
|
+
inputSchema,
|
|
347
|
+
outputSchema,
|
|
348
|
+
async execute({ connectionId, query, variables }, connections) {
|
|
349
|
+
const connection2 = connections.find((c) => c.id === connectionId);
|
|
350
|
+
if (!connection2) {
|
|
351
|
+
return {
|
|
352
|
+
success: false,
|
|
353
|
+
error: `Connection ${connectionId} not found`
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
console.log(
|
|
357
|
+
`[connector-request] linear/${connection2.name}: GraphQL request`
|
|
358
|
+
);
|
|
359
|
+
try {
|
|
360
|
+
const apiKey = parameters.apiKey.getValue(connection2);
|
|
361
|
+
const controller = new AbortController();
|
|
362
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
363
|
+
try {
|
|
364
|
+
const body = { query };
|
|
365
|
+
if (variables) body.variables = variables;
|
|
366
|
+
const response = await fetch(BASE_URL2, {
|
|
367
|
+
method: "POST",
|
|
368
|
+
headers: {
|
|
369
|
+
Authorization: apiKey,
|
|
370
|
+
"Content-Type": "application/json"
|
|
371
|
+
},
|
|
372
|
+
body: JSON.stringify(body),
|
|
373
|
+
signal: controller.signal
|
|
374
|
+
});
|
|
375
|
+
const result = await response.json();
|
|
376
|
+
if (!response.ok) {
|
|
377
|
+
const errorMessage = typeof result?.message === "string" ? result.message : typeof result?.error === "string" ? result.error : `HTTP ${response.status} ${response.statusText}`;
|
|
378
|
+
return { success: false, error: errorMessage };
|
|
379
|
+
}
|
|
380
|
+
if (result.errors && Array.isArray(result.errors) && result.errors.length > 0) {
|
|
381
|
+
const messages = result.errors.map((e) => e.message).join("; ");
|
|
382
|
+
if (!result.data) {
|
|
383
|
+
return { success: false, error: messages };
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
success: true,
|
|
387
|
+
data: { ...result, _warnings: messages }
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
success: true,
|
|
392
|
+
data: result.data ?? result
|
|
393
|
+
};
|
|
394
|
+
} finally {
|
|
395
|
+
clearTimeout(timeout);
|
|
396
|
+
}
|
|
397
|
+
} catch (err) {
|
|
398
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
399
|
+
return { success: false, error: msg };
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// ../connectors/src/connectors/linear/index.ts
|
|
405
|
+
var tools = { request: requestTool };
|
|
406
|
+
var linearConnector = new ConnectorPlugin({
|
|
407
|
+
slug: "linear",
|
|
408
|
+
authType: AUTH_TYPES.API_KEY,
|
|
409
|
+
name: "Linear",
|
|
410
|
+
description: "Connect to Linear for project management data \u2014 issues, projects, teams, cycles, and more.",
|
|
411
|
+
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/6oR77h6TeniXGdmnp2P2LX/a2ac8630ae52d164363adb0c695d9f0b/linear.webp",
|
|
412
|
+
parameters,
|
|
413
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
414
|
+
onboarding: linearOnboarding,
|
|
415
|
+
systemPrompt: {
|
|
416
|
+
en: `### Tools
|
|
417
|
+
|
|
418
|
+
- \`linear_request\`: The only way to call the Linear GraphQL API. Use it to query and mutate all Linear resources: issues, projects, teams, cycles, users, labels, workflow states, and comments. Authentication is configured automatically. Linear's API is GraphQL-only \u2014 send a query string and optional variables. Pagination uses Relay-style cursors with \`first\`/\`after\` and \`last\`/\`before\` arguments. Archived resources are hidden by default; pass \`includeArchived: true\` to include them.
|
|
419
|
+
|
|
420
|
+
### Business Logic
|
|
421
|
+
|
|
422
|
+
The business logic type for this connector is "typescript". Use the connector SDK in your handler. Do NOT read credentials from environment variables.
|
|
423
|
+
|
|
424
|
+
SDK methods (client created via \`connection(connectionId)\`):
|
|
425
|
+
- \`client.graphql(query, variables?)\` \u2014 send any GraphQL query/mutation and get typed data back
|
|
426
|
+
- \`client.request(init?)\` \u2014 low-level authenticated fetch to the GraphQL endpoint
|
|
427
|
+
- \`client.listTeams(options?)\` \u2014 list teams in the workspace
|
|
428
|
+
- \`client.listIssues(options?)\` \u2014 list issues with optional teamId filter, pagination, ordering
|
|
429
|
+
- \`client.getIssue(id)\` \u2014 fetch a single issue by ID or identifier (e.g. "TEAM-123")
|
|
430
|
+
- \`client.createIssue(input)\` \u2014 create a new issue (requires title and teamId)
|
|
431
|
+
- \`client.listProjects(options?)\` \u2014 list projects with pagination
|
|
432
|
+
|
|
433
|
+
\`\`\`ts
|
|
434
|
+
import type { Context } from "hono";
|
|
435
|
+
import { connection } from "@squadbase/vite-server/connectors/linear";
|
|
436
|
+
|
|
437
|
+
const linear = connection("<connectionId>");
|
|
438
|
+
|
|
439
|
+
export default async function handler(c: Context) {
|
|
440
|
+
const { teamId, first = 20 } = await c.req.json<{
|
|
441
|
+
teamId?: string;
|
|
442
|
+
first?: number;
|
|
443
|
+
}>();
|
|
444
|
+
|
|
445
|
+
const { nodes, pageInfo } = await linear.listIssues({ teamId, first });
|
|
446
|
+
|
|
447
|
+
return c.json({ issues: nodes, pageInfo });
|
|
448
|
+
}
|
|
449
|
+
\`\`\`
|
|
450
|
+
|
|
451
|
+
### Linear GraphQL API Reference
|
|
452
|
+
|
|
453
|
+
- Endpoint: \`https://api.linear.app/graphql\`
|
|
454
|
+
- Authentication: API key passed as \`Authorization: <API_KEY>\` header (handled automatically)
|
|
455
|
+
- All requests are POST with JSON body: \`{ "query": "...", "variables": {...} }\`
|
|
456
|
+
- Rate limit: 1,500 requests per hour with authentication
|
|
457
|
+
- Pagination: Relay-style cursor-based \u2014 use \`first\`/\`after\` (forward) or \`last\`/\`before\` (backward); responses include \`pageInfo { hasNextPage endCursor }\`
|
|
458
|
+
- Archived resources: hidden by default; pass \`includeArchived: true\` to include them
|
|
459
|
+
- Issue creation: if no \`stateId\` is given, the issue is assigned to the team's first workflow state (or Triage if enabled)
|
|
460
|
+
- GraphQL responses may partially succeed (HTTP 200 with both \`data\` and \`errors\`)
|
|
461
|
+
|
|
462
|
+
#### Common Queries
|
|
463
|
+
- \`{ viewer { id name email } }\` \u2014 current authenticated user
|
|
464
|
+
- \`{ teams { nodes { id name key } } }\` \u2014 list all teams
|
|
465
|
+
- \`{ issues(first: 10, orderBy: updatedAt) { nodes { id identifier title state { name } assignee { name } priority } pageInfo { hasNextPage endCursor } } }\` \u2014 list recent issues
|
|
466
|
+
- \`{ issue(id: "TEAM-123") { id identifier title description state { name } assignee { name } } }\` \u2014 get a single issue by identifier
|
|
467
|
+
- \`{ projects(first: 10) { nodes { id name state startDate targetDate } } }\` \u2014 list projects
|
|
468
|
+
- \`{ cycles(first: 10) { nodes { id name number startsAt endsAt } } }\` \u2014 list cycles
|
|
469
|
+
- \`{ workflowStates { nodes { id name type team { name } } } }\` \u2014 list workflow states
|
|
470
|
+
- \`{ users { nodes { id name email } } }\` \u2014 list users
|
|
471
|
+
- \`{ issueLabels { nodes { id name color } } }\` \u2014 list labels
|
|
472
|
+
|
|
473
|
+
#### Common Mutations
|
|
474
|
+
- \`mutation { issueCreate(input: { title: "...", teamId: "..." }) { success issue { id identifier title } } }\` \u2014 create issue
|
|
475
|
+
- \`mutation { issueUpdate(id: "...", input: { stateId: "..." }) { success issue { id title state { name } } } }\` \u2014 update issue
|
|
476
|
+
- \`mutation { commentCreate(input: { issueId: "...", body: "..." }) { success comment { id body } } }\` \u2014 add comment`,
|
|
477
|
+
ja: `### \u30C4\u30FC\u30EB
|
|
478
|
+
|
|
479
|
+
- \`linear_request\`: Linear GraphQL API\u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002Issue\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30C1\u30FC\u30E0\u3001\u30B5\u30A4\u30AF\u30EB\u3001\u30E6\u30FC\u30B6\u30FC\u3001\u30E9\u30D9\u30EB\u3001\u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u72B6\u614B\u3001\u30B3\u30E1\u30F3\u30C8\u306A\u3069\u3059\u3079\u3066\u306ELinear\u30EA\u30BD\u30FC\u30B9\u306E\u30AF\u30A8\u30EA\u3068\u30DF\u30E5\u30FC\u30C6\u30FC\u30B7\u30E7\u30F3\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\u306F\u81EA\u52D5\u7684\u306B\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002Linear\u306EAPI\u306FGraphQL\u306E\u307F\u3067\u3059\u3002\u30AF\u30A8\u30EA\u6587\u5B57\u5217\u3068\u30AA\u30D7\u30B7\u30E7\u30F3\u306E\u5909\u6570\u3092\u9001\u4FE1\u3057\u307E\u3059\u3002\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u306FRelay\u5F62\u5F0F\u306E\u30AB\u30FC\u30BD\u30EB\u30D9\u30FC\u30B9\u3067\u3001\`first\`/\`after\`\u304A\u3088\u3073\`last\`/\`before\`\u5F15\u6570\u3092\u4F7F\u7528\u3057\u307E\u3059\u3002\u30A2\u30FC\u30AB\u30A4\u30D6\u3055\u308C\u305F\u30EA\u30BD\u30FC\u30B9\u306F\u30C7\u30D5\u30A9\u30EB\u30C8\u3067\u975E\u8868\u793A\u3067\u3059\u3002\`includeArchived: true\`\u3092\u6E21\u3059\u3068\u542B\u3081\u3089\u308C\u307E\u3059\u3002
|
|
480
|
+
|
|
481
|
+
### Business Logic
|
|
482
|
+
|
|
483
|
+
\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
|
|
484
|
+
|
|
485
|
+
SDK\u30E1\u30BD\u30C3\u30C9 (\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8):
|
|
486
|
+
- \`client.graphql(query, variables?)\` \u2014 \u4EFB\u610F\u306EGraphQL\u30AF\u30A8\u30EA/\u30DF\u30E5\u30FC\u30C6\u30FC\u30B7\u30E7\u30F3\u3092\u9001\u4FE1\u3057\u578B\u4ED8\u304D\u30C7\u30FC\u30BF\u3092\u53D6\u5F97
|
|
487
|
+
- \`client.request(init?)\` \u2014 GraphQL\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u4F4E\u30EC\u30D9\u30EB\u8A8D\u8A3C\u4ED8\u304Dfetch
|
|
488
|
+
- \`client.listTeams(options?)\` \u2014 \u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306E\u30C1\u30FC\u30E0\u4E00\u89A7\u3092\u53D6\u5F97
|
|
489
|
+
- \`client.listIssues(options?)\` \u2014 teamId\u30D5\u30A3\u30EB\u30BF\u3001\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u3001\u30BD\u30FC\u30C8\u4ED8\u304D\u3067Issue\u4E00\u89A7\u3092\u53D6\u5F97
|
|
490
|
+
- \`client.getIssue(id)\` \u2014 ID\u307E\u305F\u306F\u8B58\u5225\u5B50\uFF08\u4F8B: "TEAM-123"\uFF09\u3067\u5358\u4E00Issue\u3092\u53D6\u5F97
|
|
491
|
+
- \`client.createIssue(input)\` \u2014 \u65B0\u3057\u3044Issue\u3092\u4F5C\u6210\uFF08title\u3068teamId\u304C\u5FC5\u9808\uFF09
|
|
492
|
+
- \`client.listProjects(options?)\` \u2014 \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u4ED8\u304D\u3067\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7\u3092\u53D6\u5F97
|
|
493
|
+
|
|
494
|
+
\`\`\`ts
|
|
495
|
+
import type { Context } from "hono";
|
|
496
|
+
import { connection } from "@squadbase/vite-server/connectors/linear";
|
|
497
|
+
|
|
498
|
+
const linear = connection("<connectionId>");
|
|
499
|
+
|
|
500
|
+
export default async function handler(c: Context) {
|
|
501
|
+
const { teamId, first = 20 } = await c.req.json<{
|
|
502
|
+
teamId?: string;
|
|
503
|
+
first?: number;
|
|
504
|
+
}>();
|
|
505
|
+
|
|
506
|
+
const { nodes, pageInfo } = await linear.listIssues({ teamId, first });
|
|
507
|
+
|
|
508
|
+
return c.json({ issues: nodes, pageInfo });
|
|
509
|
+
}
|
|
510
|
+
\`\`\`
|
|
511
|
+
|
|
512
|
+
### Linear GraphQL API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
|
|
513
|
+
|
|
514
|
+
- \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8: \`https://api.linear.app/graphql\`
|
|
515
|
+
- \u8A8D\u8A3C: API\u30AD\u30FC\u3092 \`Authorization: <API_KEY>\` \u30D8\u30C3\u30C0\u30FC\u3067\u9001\u4FE1\uFF08\u81EA\u52D5\u8A2D\u5B9A\uFF09
|
|
516
|
+
- \u3059\u3079\u3066\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u306FPOST\u3067JSON\u30DC\u30C7\u30A3: \`{ "query": "...", "variables": {...} }\`
|
|
517
|
+
- \u30EC\u30FC\u30C8\u5236\u9650: \u8A8D\u8A3C\u6E08\u307F\u30671\u6642\u9593\u3042\u305F\u308A1,500\u30EA\u30AF\u30A8\u30B9\u30C8
|
|
518
|
+
- \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3: Relay\u5F62\u5F0F\u30AB\u30FC\u30BD\u30EB\u30D9\u30FC\u30B9 \u2014 \`first\`/\`after\`\uFF08\u524D\u65B9\uFF09\u307E\u305F\u306F\`last\`/\`before\`\uFF08\u5F8C\u65B9\uFF09\u3092\u4F7F\u7528\u3002\u30EC\u30B9\u30DD\u30F3\u30B9\u306B\u306F \`pageInfo { hasNextPage endCursor }\` \u304C\u542B\u307E\u308C\u308B
|
|
519
|
+
- \u30A2\u30FC\u30AB\u30A4\u30D6\u3055\u308C\u305F\u30EA\u30BD\u30FC\u30B9: \u30C7\u30D5\u30A9\u30EB\u30C8\u3067\u975E\u8868\u793A\u3002\`includeArchived: true\` \u3067\u542B\u3081\u308B
|
|
520
|
+
- Issue\u4F5C\u6210: \`stateId\` \u3092\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u3001\u30C1\u30FC\u30E0\u306E\u6700\u521D\u306E\u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u72B6\u614B\uFF08\u307E\u305F\u306FTriage\u304C\u6709\u52B9\u306A\u5834\u5408\u306FTriage\uFF09\u306B\u5272\u308A\u5F53\u3066\u3089\u308C\u308B
|
|
521
|
+
- GraphQL\u30EC\u30B9\u30DD\u30F3\u30B9\u306F\u90E8\u5206\u7684\u306B\u6210\u529F\u3059\u308B\u5834\u5408\u304C\u3042\u308B\uFF08HTTP 200\u3067\`data\`\u3068\`errors\`\u306E\u4E21\u65B9\u304C\u542B\u307E\u308C\u308B\uFF09
|
|
522
|
+
|
|
523
|
+
#### \u4E3B\u8981\u30AF\u30A8\u30EA
|
|
524
|
+
- \`{ viewer { id name email } }\` \u2014 \u73FE\u5728\u306E\u8A8D\u8A3C\u30E6\u30FC\u30B6\u30FC
|
|
525
|
+
- \`{ teams { nodes { id name key } } }\` \u2014 \u30C1\u30FC\u30E0\u4E00\u89A7
|
|
526
|
+
- \`{ issues(first: 10, orderBy: updatedAt) { nodes { id identifier title state { name } assignee { name } priority } pageInfo { hasNextPage endCursor } } }\` \u2014 \u6700\u8FD1\u306EIssue\u4E00\u89A7
|
|
527
|
+
- \`{ issue(id: "TEAM-123") { id identifier title description state { name } assignee { name } } }\` \u2014 \u8B58\u5225\u5B50\u3067\u5358\u4E00Issue\u3092\u53D6\u5F97
|
|
528
|
+
- \`{ projects(first: 10) { nodes { id name state startDate targetDate } } }\` \u2014 \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u4E00\u89A7
|
|
529
|
+
- \`{ cycles(first: 10) { nodes { id name number startsAt endsAt } } }\` \u2014 \u30B5\u30A4\u30AF\u30EB\u4E00\u89A7
|
|
530
|
+
- \`{ workflowStates { nodes { id name type team { name } } } }\` \u2014 \u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u72B6\u614B\u4E00\u89A7
|
|
531
|
+
- \`{ users { nodes { id name email } } }\` \u2014 \u30E6\u30FC\u30B6\u30FC\u4E00\u89A7
|
|
532
|
+
- \`{ issueLabels { nodes { id name color } } }\` \u2014 \u30E9\u30D9\u30EB\u4E00\u89A7
|
|
533
|
+
|
|
534
|
+
#### \u4E3B\u8981\u30DF\u30E5\u30FC\u30C6\u30FC\u30B7\u30E7\u30F3
|
|
535
|
+
- \`mutation { issueCreate(input: { title: "...", teamId: "..." }) { success issue { id identifier title } } }\` \u2014 Issue\u4F5C\u6210
|
|
536
|
+
- \`mutation { issueUpdate(id: "...", input: { stateId: "..." }) { success issue { id title state { name } } } }\` \u2014 Issue\u66F4\u65B0
|
|
537
|
+
- \`mutation { commentCreate(input: { issueId: "...", body: "..." }) { success comment { id body } } }\` \u2014 \u30B3\u30E1\u30F3\u30C8\u8FFD\u52A0`
|
|
538
|
+
},
|
|
539
|
+
tools
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// src/connectors/create-connector-sdk.ts
|
|
543
|
+
import { readFileSync } from "fs";
|
|
544
|
+
import path from "path";
|
|
545
|
+
|
|
546
|
+
// src/connector-client/env.ts
|
|
547
|
+
function resolveEnvVar(entry, key, connectionId) {
|
|
548
|
+
const envVarName = entry.envVars[key];
|
|
549
|
+
if (!envVarName) {
|
|
550
|
+
throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
|
|
551
|
+
}
|
|
552
|
+
const value = process.env[envVarName];
|
|
553
|
+
if (!value) {
|
|
554
|
+
throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
|
|
555
|
+
}
|
|
556
|
+
return value;
|
|
557
|
+
}
|
|
558
|
+
function resolveEnvVarOptional(entry, key) {
|
|
559
|
+
const envVarName = entry.envVars[key];
|
|
560
|
+
if (!envVarName) return void 0;
|
|
561
|
+
return process.env[envVarName] || void 0;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/connector-client/proxy-fetch.ts
|
|
565
|
+
import { getContext } from "hono/context-storage";
|
|
566
|
+
import { getCookie } from "hono/cookie";
|
|
567
|
+
var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
|
|
568
|
+
function normalizeHeaders(input) {
|
|
569
|
+
const out = {};
|
|
570
|
+
if (!input) return out;
|
|
571
|
+
new Headers(input).forEach((value, key) => {
|
|
572
|
+
out[key] = value;
|
|
573
|
+
});
|
|
574
|
+
return out;
|
|
575
|
+
}
|
|
576
|
+
function createSandboxProxyFetch(connectionId) {
|
|
577
|
+
return async (input, init) => {
|
|
578
|
+
const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
|
|
579
|
+
const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
|
|
580
|
+
if (!token || !sandboxId) {
|
|
581
|
+
throw new Error(
|
|
582
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
586
|
+
const originalMethod = init?.method ?? "GET";
|
|
587
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
588
|
+
const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
|
|
589
|
+
const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
590
|
+
return fetch(proxyUrl, {
|
|
591
|
+
method: "POST",
|
|
592
|
+
headers: {
|
|
593
|
+
"Content-Type": "application/json",
|
|
594
|
+
Authorization: `Bearer ${token}`
|
|
595
|
+
},
|
|
596
|
+
body: JSON.stringify({
|
|
597
|
+
url: originalUrl,
|
|
598
|
+
method: originalMethod,
|
|
599
|
+
headers: normalizeHeaders(init?.headers),
|
|
600
|
+
body: originalBody
|
|
601
|
+
})
|
|
602
|
+
});
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
function createDeployedAppProxyFetch(connectionId) {
|
|
606
|
+
const projectId = process.env["SQUADBASE_PROJECT_ID"];
|
|
607
|
+
if (!projectId) {
|
|
608
|
+
throw new Error(
|
|
609
|
+
"Connection proxy is not configured. Please check your deployment settings."
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
|
|
613
|
+
const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
614
|
+
return async (input, init) => {
|
|
615
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
616
|
+
const originalMethod = init?.method ?? "GET";
|
|
617
|
+
const originalBody = init?.body ? JSON.parse(init.body) : void 0;
|
|
618
|
+
const c = getContext();
|
|
619
|
+
const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
|
|
620
|
+
if (!appSession) {
|
|
621
|
+
throw new Error(
|
|
622
|
+
"No authentication method available for connection proxy."
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
return fetch(proxyUrl, {
|
|
626
|
+
method: "POST",
|
|
627
|
+
headers: {
|
|
628
|
+
"Content-Type": "application/json",
|
|
629
|
+
Authorization: `Bearer ${appSession}`
|
|
630
|
+
},
|
|
631
|
+
body: JSON.stringify({
|
|
632
|
+
url: originalUrl,
|
|
633
|
+
method: originalMethod,
|
|
634
|
+
headers: normalizeHeaders(init?.headers),
|
|
635
|
+
body: originalBody
|
|
636
|
+
})
|
|
637
|
+
});
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function createProxyFetch(connectionId) {
|
|
641
|
+
if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
|
|
642
|
+
return createSandboxProxyFetch(connectionId);
|
|
643
|
+
}
|
|
644
|
+
return createDeployedAppProxyFetch(connectionId);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/connectors/create-connector-sdk.ts
|
|
648
|
+
function loadConnectionsSync() {
|
|
649
|
+
const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
|
|
650
|
+
try {
|
|
651
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
652
|
+
return JSON.parse(raw);
|
|
653
|
+
} catch {
|
|
654
|
+
return {};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
function createConnectorSdk(plugin, createClient2) {
|
|
658
|
+
return (connectionId) => {
|
|
659
|
+
const connections = loadConnectionsSync();
|
|
660
|
+
const entry = connections[connectionId];
|
|
661
|
+
if (!entry) {
|
|
662
|
+
throw new Error(
|
|
663
|
+
`Connection "${connectionId}" not found in .squadbase/connections.json`
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
if (entry.connector.slug !== plugin.slug) {
|
|
667
|
+
throw new Error(
|
|
668
|
+
`Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
const params = {};
|
|
672
|
+
for (const param of Object.values(plugin.parameters)) {
|
|
673
|
+
if (param.required) {
|
|
674
|
+
params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
|
|
675
|
+
} else {
|
|
676
|
+
const val = resolveEnvVarOptional(entry, param.slug);
|
|
677
|
+
if (val !== void 0) params[param.slug] = val;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return createClient2(params, createProxyFetch(connectionId));
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// src/connectors/entries/linear.ts
|
|
685
|
+
var connection = createConnectorSdk(linearConnector, createClient);
|
|
686
|
+
export {
|
|
687
|
+
connection
|
|
688
|
+
};
|