@nimbus-dev/connectors 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -4
- package/connectors/airflow/src/search-filter.ts +2 -15
- package/connectors/airflow/src/server.ts +2 -87
- package/connectors/airflow/src/tools.ts +74 -0
- package/connectors/apple/src/adapters.ts +326 -0
- package/connectors/apple/src/apple-mail-core.ts +4 -8
- package/connectors/apple/src/calendar-tools.ts +28 -8
- package/connectors/apple/src/server.ts +23 -512
- package/connectors/apple/src/tools.ts +18 -44
- package/connectors/argocd/src/server.ts +25 -38
- package/connectors/athena/src/tools.ts +15 -34
- package/connectors/aws/src/server.ts +2 -183
- package/connectors/aws/src/tools.ts +197 -0
- package/connectors/azure/src/server.ts +2 -131
- package/connectors/azure/src/tools.ts +133 -0
- package/connectors/bigeye/src/search-filter.ts +4 -15
- package/connectors/bigeye/src/server.ts +38 -38
- package/connectors/bigquery/src/tools.ts +1 -26
- package/connectors/bitbucket/src/server.ts +2 -244
- package/connectors/bitbucket/src/tools.ts +241 -0
- package/connectors/bitrise/src/server.ts +2 -79
- package/connectors/bitrise/src/tools.ts +76 -0
- package/connectors/canva/src/search-filter.ts +2 -11
- package/connectors/canva/src/server.ts +2 -73
- package/connectors/canva/src/tools.ts +66 -0
- package/connectors/circleci/src/server.ts +2 -176
- package/connectors/circleci/src/tools.ts +193 -0
- package/connectors/cloud-logging/src/tools.ts +14 -27
- package/connectors/cloudwatch/src/tools.ts +15 -34
- package/connectors/codemagic/src/server.ts +2 -70
- package/connectors/codemagic/src/tools.ts +71 -0
- package/connectors/confluence/src/server.ts +2 -301
- package/connectors/confluence/src/tools.ts +320 -0
- package/connectors/dagster/src/server.ts +2 -214
- package/connectors/dagster/src/tools.ts +187 -0
- package/connectors/databricks/src/search-filter.ts +7 -7
- package/connectors/databricks/src/server.ts +2 -77
- package/connectors/databricks/src/tools.ts +66 -0
- package/connectors/datadog/src/server.ts +2 -48
- package/connectors/datadog/src/tools.ts +48 -0
- package/connectors/dataprofile/src/profile.ts +16 -46
- package/connectors/dbt/src/server.ts +2 -92
- package/connectors/dbt/src/tools.ts +80 -0
- package/connectors/dependencytrack/src/server.ts +2 -76
- package/connectors/dependencytrack/src/tools.ts +65 -0
- package/connectors/discord/src/server.ts +2 -85
- package/connectors/discord/src/tools.ts +82 -0
- package/connectors/elasticsearch/src/tools.ts +7 -37
- package/connectors/fastmail/src/jmap-client.ts +202 -0
- package/connectors/fastmail/src/jmap-core.ts +8 -21
- package/connectors/fastmail/src/server.ts +2 -187
- package/connectors/fastmail/src/tools.ts +4 -23
- package/connectors/figma/src/search-filter.ts +2 -11
- package/connectors/figma/src/server.ts +2 -113
- package/connectors/figma/src/tools.ts +99 -0
- package/connectors/firebase/src/server.ts +2 -90
- package/connectors/firebase/src/token.ts +2 -5
- package/connectors/firebase/src/tools.ts +106 -0
- package/connectors/flagsmith/src/server.ts +2 -98
- package/connectors/flagsmith/src/tools.ts +95 -0
- package/connectors/flux/src/server.ts +52 -53
- package/connectors/gcp/src/server.ts +2 -132
- package/connectors/gcp/src/tools.ts +123 -0
- package/connectors/github/src/server.ts +2 -328
- package/connectors/github/src/tools.ts +311 -0
- package/connectors/github-actions/src/server.ts +2 -205
- package/connectors/github-actions/src/tools.ts +209 -0
- package/connectors/gitlab/src/server.ts +2 -384
- package/connectors/gitlab/src/tools.ts +392 -0
- package/connectors/gmail/src/server.ts +2 -270
- package/connectors/gmail/src/tools.ts +209 -0
- package/connectors/google-drive/src/server.ts +2 -524
- package/connectors/google-drive/src/tools.ts +529 -0
- package/connectors/google-meet/src/server.ts +2 -82
- package/connectors/google-meet/src/tools.ts +87 -0
- package/connectors/google-photos/src/server.ts +2 -132
- package/connectors/google-photos/src/tools.ts +138 -0
- package/connectors/grafana/src/server.ts +2 -53
- package/connectors/grafana/src/tools.ts +53 -0
- package/connectors/great-expectations/src/gx-parse.ts +14 -45
- package/connectors/greenhouse/src/server.ts +2 -63
- package/connectors/greenhouse/src/tools.ts +68 -0
- package/connectors/hubspot/src/server.ts +2 -77
- package/connectors/hubspot/src/tools.ts +70 -0
- package/connectors/iac/src/server.ts +2 -137
- package/connectors/iac/src/tools.ts +152 -0
- package/connectors/imap/src/imap-core.ts +29 -82
- package/connectors/imap/src/server.ts +33 -220
- package/connectors/intercom/src/server.ts +2 -61
- package/connectors/intercom/src/tools.ts +58 -0
- package/connectors/jenkins/src/jenkins-api.ts +2 -6
- package/connectors/jenkins/src/server.ts +2 -215
- package/connectors/jenkins/src/tools.ts +240 -0
- package/connectors/jira/src/server.ts +2 -301
- package/connectors/jira/src/tools.ts +325 -0
- package/connectors/kubernetes/src/server.ts +2 -155
- package/connectors/kubernetes/src/tools.ts +159 -0
- package/connectors/launchdarkly/src/server.ts +2 -75
- package/connectors/launchdarkly/src/tools.ts +72 -0
- package/connectors/lever/src/search-filter.ts +4 -12
- package/connectors/lever/src/server.ts +2 -60
- package/connectors/lever/src/tools.ts +61 -0
- package/connectors/linear/src/server.ts +2 -459
- package/connectors/linear/src/tools.ts +482 -0
- package/connectors/localdb/src/sql-scan.ts +13 -41
- package/connectors/localdb/src/tools.ts +1 -1
- package/connectors/looker/src/search-filter.ts +2 -15
- package/connectors/looker/src/server.ts +35 -70
- package/connectors/mendeley/src/server.ts +2 -55
- package/connectors/mendeley/src/tools.ts +54 -0
- package/connectors/mercury/src/server.ts +2 -57
- package/connectors/mercury/src/tools.ts +50 -0
- package/connectors/metabase/src/search-filter.ts +4 -15
- package/connectors/metabase/src/server.ts +2 -79
- package/connectors/metabase/src/tools.ts +64 -0
- package/connectors/miro/src/server.ts +2 -72
- package/connectors/miro/src/tools.ts +65 -0
- package/connectors/mlflow/src/search-filter.ts +3 -7
- package/connectors/mlflow/src/server.ts +20 -40
- package/connectors/monte-carlo/src/server.ts +50 -57
- package/connectors/netlify/src/search-filter.ts +6 -14
- package/connectors/netlify/src/server.ts +2 -60
- package/connectors/netlify/src/tools.ts +53 -0
- package/connectors/newrelic/src/server.ts +2 -40
- package/connectors/newrelic/src/tools.ts +37 -0
- package/connectors/notion/src/server.ts +2 -335
- package/connectors/notion/src/tools.ts +344 -0
- package/connectors/obsidian/src/server.ts +2 -425
- package/connectors/obsidian/src/tools.ts +466 -0
- package/connectors/onedrive/src/server.ts +2 -249
- package/connectors/onedrive/src/tools.ts +253 -0
- package/connectors/outlook/src/server.ts +2 -351
- package/connectors/outlook/src/tools.ts +357 -0
- package/connectors/pagerduty/src/server.ts +2 -132
- package/connectors/pagerduty/src/tools.ts +126 -0
- package/connectors/pipedrive/src/server.ts +2 -58
- package/connectors/pipedrive/src/tools.ts +63 -0
- package/connectors/powerbi/src/search-filter.ts +2 -15
- package/connectors/powerbi/src/server.ts +43 -68
- package/connectors/prefect/src/search-filter.ts +3 -16
- package/connectors/prefect/src/server.ts +2 -90
- package/connectors/prefect/src/tools.ts +83 -0
- package/connectors/protonmail/src/mail-core.ts +16 -75
- package/connectors/protonmail/src/server.ts +34 -214
- package/connectors/raindrop/src/server.ts +2 -103
- package/connectors/raindrop/src/tools.ts +103 -0
- package/connectors/ramp/src/server.ts +2 -105
- package/connectors/ramp/src/tools.ts +98 -0
- package/connectors/readwise/src/server.ts +2 -88
- package/connectors/readwise/src/tools.ts +81 -0
- package/connectors/sagemaker/src/tools.ts +19 -38
- package/connectors/salesforce/src/search-filter.ts +4 -11
- package/connectors/salesforce/src/server.ts +2 -90
- package/connectors/salesforce/src/tools.ts +75 -0
- package/connectors/semgrep/src/search-filter.ts +3 -10
- package/connectors/semgrep/src/server.ts +2 -104
- package/connectors/semgrep/src/tools.ts +92 -0
- package/connectors/sentry/src/server.ts +2 -58
- package/connectors/sentry/src/tools.ts +53 -0
- package/connectors/slack/src/server.ts +2 -352
- package/connectors/slack/src/tools.ts +376 -0
- package/connectors/snowflake/src/search-filter.ts +4 -19
- package/connectors/snowflake/src/server.ts +6 -25
- package/connectors/snyk/src/server.ts +2 -140
- package/connectors/snyk/src/tools.ts +136 -0
- package/connectors/sonarqube/src/server.ts +2 -111
- package/connectors/sonarqube/src/tools.ts +98 -0
- package/connectors/stackoverflow/src/server.ts +2 -77
- package/connectors/stackoverflow/src/tools.ts +66 -0
- package/connectors/storybook/src/storybook-parse.ts +3 -6
- package/connectors/stripe/src/server.ts +2 -61
- package/connectors/stripe/src/tools.ts +54 -0
- package/connectors/superset/src/search-filter.ts +4 -15
- package/connectors/superset/src/server.ts +2 -115
- package/connectors/superset/src/tools.ts +98 -0
- package/connectors/tableau/src/search-filter.ts +2 -15
- package/connectors/tableau/src/server.ts +13 -65
- package/connectors/teams/src/server.ts +2 -249
- package/connectors/teams/src/tools.ts +283 -0
- package/connectors/testflight/src/server.ts +2 -70
- package/connectors/testflight/src/tools.ts +79 -0
- package/connectors/vercel/src/search-filter.ts +2 -10
- package/connectors/vercel/src/server.ts +2 -75
- package/connectors/vercel/src/tools.ts +73 -0
- package/connectors/vertex-ai/src/tools.ts +23 -33
- package/connectors/wiz/src/search-filter.ts +6 -30
- package/connectors/wiz/src/server.ts +2 -188
- package/connectors/wiz/src/tools.ts +168 -0
- package/connectors/zendesk/src/server.ts +2 -73
- package/connectors/zendesk/src/tools.ts +60 -0
- package/connectors/zoom/src/search-filter.ts +1 -7
- package/connectors/zoom/src/server.ts +2 -94
- package/connectors/zoom/src/tools.ts +93 -0
- package/connectors/zotero/src/server.ts +2 -78
- package/connectors/zotero/src/tools.ts +67 -0
- package/package.json +14 -12
- package/shared/access-token-cache.ts +123 -0
- package/shared/atlassian-json-fetch.ts +6 -16
- package/shared/audit-chain.ts +13 -6
- package/shared/cli-json-kit.ts +150 -0
- package/shared/collection-tool-kit.ts +143 -0
- package/shared/connector-mode.ts +3 -2
- package/shared/cursor-list-tool.ts +29 -0
- package/shared/env-json-api.ts +209 -0
- package/shared/fetch-json-text.ts +27 -0
- package/shared/github-rest.ts +62 -0
- package/shared/graphql-json.ts +60 -0
- package/shared/imap-mail-core.ts +91 -0
- package/shared/imap-tool-kit.ts +57 -23
- package/shared/imapflow-adapter.ts +417 -0
- package/shared/rest-tool-kit.ts +77 -7
- package/shared/run-cli-json.ts +38 -16
- package/shared/search-filter.ts +58 -18
- package/shared/walk-files.ts +118 -0
- package/standalone/src/launcher.ts +19 -7
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ConsentServer } from "../../../shared/consent-kit.ts";
|
|
3
|
+
import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
4
|
+
import {
|
|
5
|
+
fetchBearerAuthorizedJson,
|
|
6
|
+
resolveUrlWithBase,
|
|
7
|
+
} from "../../../shared/fetch-bearer-json.ts";
|
|
8
|
+
import {
|
|
9
|
+
createRegisterSimpleTool,
|
|
10
|
+
createZodToolRegistrar,
|
|
11
|
+
type McpListResult,
|
|
12
|
+
mcpJsonResultIfOk,
|
|
13
|
+
requireProcessEnv,
|
|
14
|
+
} from "../../../shared/mcp-tool-kit.ts";
|
|
15
|
+
import { teamsBotSendActivity } from "./bot-send.ts";
|
|
16
|
+
|
|
17
|
+
const GRAPH = "https://graph.microsoft.com/v1.0";
|
|
18
|
+
|
|
19
|
+
async function graphRequest(
|
|
20
|
+
token: string,
|
|
21
|
+
pathOrUrl: string,
|
|
22
|
+
init?: RequestInit,
|
|
23
|
+
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
24
|
+
const url = resolveUrlWithBase(GRAPH, pathOrUrl);
|
|
25
|
+
return fetchBearerAuthorizedJson(url, token, init);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function teamsPagedGraph(
|
|
29
|
+
token: string,
|
|
30
|
+
nextLink: string | undefined,
|
|
31
|
+
initialPath: string,
|
|
32
|
+
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
33
|
+
const path = nextLink !== undefined && nextLink !== "" ? nextLink : initialPath;
|
|
34
|
+
return graphRequest(token, path);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function graphListResult(r: {
|
|
38
|
+
ok: boolean;
|
|
39
|
+
status: number;
|
|
40
|
+
json: unknown;
|
|
41
|
+
text: string;
|
|
42
|
+
}): McpListResult {
|
|
43
|
+
return mcpJsonResultIfOk("Graph", r, 200);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** POST one message to a Graph `…/messages` collection — a team channel's or a chat's. */
|
|
47
|
+
async function postGraphMessage(
|
|
48
|
+
token: string,
|
|
49
|
+
messagesPath: string,
|
|
50
|
+
content: string,
|
|
51
|
+
contentType: "text" | "html" = "text",
|
|
52
|
+
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
53
|
+
return graphRequest(token, messagesPath, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "Content-Type": "application/json" },
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
body: {
|
|
58
|
+
contentType: contentType === "html" ? "html" : "text",
|
|
59
|
+
content,
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
66
|
+
export const TEAMS_TOOL_NAMES = [
|
|
67
|
+
"teams_team_list",
|
|
68
|
+
"teams_channel_list",
|
|
69
|
+
"teams_channel_messages",
|
|
70
|
+
"teams_chat_list",
|
|
71
|
+
"teams_chat_messages",
|
|
72
|
+
"teams_message_post",
|
|
73
|
+
"teams_message_post_chat",
|
|
74
|
+
"teams_user_info",
|
|
75
|
+
"teams_chat_post",
|
|
76
|
+
] as const;
|
|
77
|
+
|
|
78
|
+
export function registerTeamsTools(
|
|
79
|
+
server: ConsentServer & { tool: (...args: never) => unknown },
|
|
80
|
+
): void {
|
|
81
|
+
const registerSimpleTool = createRegisterSimpleTool(server);
|
|
82
|
+
|
|
83
|
+
const reg = createZodToolRegistrar(registerSimpleTool);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Every MUTATING teams tool goes through here. Outside the gateway this adds the
|
|
87
|
+
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
88
|
+
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
89
|
+
*/
|
|
90
|
+
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
91
|
+
connector: "teams",
|
|
92
|
+
scopeEnv: "NIMBUS_MCP_TEAMS_WRITE_SCOPE",
|
|
93
|
+
scopeKinds: ["channel", "chat"],
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const teamsTeamListSchema = z.object({
|
|
97
|
+
top: z.number().int().min(1).max(100).optional(),
|
|
98
|
+
nextLink: z.url().optional(),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
reg(
|
|
102
|
+
"teams_team_list",
|
|
103
|
+
"List Microsoft Teams the signed-in user has joined (pagination via nextLink).",
|
|
104
|
+
teamsTeamListSchema,
|
|
105
|
+
async (parsed) => {
|
|
106
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
107
|
+
const initial = `/me/joinedTeams?$top=${String(parsed.top ?? 50)}`;
|
|
108
|
+
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
109
|
+
return graphListResult(r);
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const teamsChannelListSchema = z.object({
|
|
114
|
+
teamId: z.string().min(1),
|
|
115
|
+
top: z.number().int().min(1).max(100).optional(),
|
|
116
|
+
nextLink: z.url().optional(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
reg(
|
|
120
|
+
"teams_channel_list",
|
|
121
|
+
"List channels in a team (standard + private the app can read).",
|
|
122
|
+
teamsChannelListSchema,
|
|
123
|
+
async (parsed) => {
|
|
124
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
125
|
+
const tid = encodeURIComponent(parsed.teamId);
|
|
126
|
+
const initial = `/teams/${tid}/channels?$top=${String(parsed.top ?? 50)}`;
|
|
127
|
+
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
128
|
+
return graphListResult(r);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const teamsChannelMessagesSchema = z.object({
|
|
133
|
+
teamId: z.string().min(1),
|
|
134
|
+
channelId: z.string().min(1),
|
|
135
|
+
top: z.number().int().min(1).max(50).optional(),
|
|
136
|
+
nextLink: z.url().optional(),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
reg(
|
|
140
|
+
"teams_channel_messages",
|
|
141
|
+
"List recent messages in a team channel (not delta; for interactive reads).",
|
|
142
|
+
teamsChannelMessagesSchema,
|
|
143
|
+
async (parsed) => {
|
|
144
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
145
|
+
const tid = encodeURIComponent(parsed.teamId);
|
|
146
|
+
const cid = encodeURIComponent(parsed.channelId);
|
|
147
|
+
const initial = `/teams/${tid}/channels/${cid}/messages?$top=${String(parsed.top ?? 25)}`;
|
|
148
|
+
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
149
|
+
return graphListResult(r);
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const teamsChatListSchema = z.object({
|
|
154
|
+
top: z.number().int().min(1).max(50).optional(),
|
|
155
|
+
nextLink: z.url().optional(),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
reg(
|
|
159
|
+
"teams_chat_list",
|
|
160
|
+
"List 1:1 and group chats for the signed-in user.",
|
|
161
|
+
teamsChatListSchema,
|
|
162
|
+
async (parsed) => {
|
|
163
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
164
|
+
const initial = `/me/chats?$top=${String(parsed.top ?? 25)}`;
|
|
165
|
+
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
166
|
+
return graphListResult(r);
|
|
167
|
+
},
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const teamsChatMessagesSchema = z.object({
|
|
171
|
+
chatId: z.string().min(1),
|
|
172
|
+
top: z.number().int().min(1).max(50).optional(),
|
|
173
|
+
nextLink: z.url().optional(),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
reg(
|
|
177
|
+
"teams_chat_messages",
|
|
178
|
+
"List messages in a chat.",
|
|
179
|
+
teamsChatMessagesSchema,
|
|
180
|
+
async (parsed) => {
|
|
181
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
182
|
+
const id = encodeURIComponent(parsed.chatId);
|
|
183
|
+
const initial = `/chats/${id}/messages?$top=${String(parsed.top ?? 25)}`;
|
|
184
|
+
const r = await teamsPagedGraph(token, parsed.nextLink, initial);
|
|
185
|
+
return graphListResult(r);
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const teamsMessagePostSchema = z.object({
|
|
190
|
+
teamId: z.string().min(1),
|
|
191
|
+
channelId: z.string().min(1),
|
|
192
|
+
body: z.string().min(1),
|
|
193
|
+
contentType: z.enum(["text", "html"]).optional(),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
registerWriteTool(
|
|
197
|
+
"teams_message_post",
|
|
198
|
+
{
|
|
199
|
+
mutates: "teams.message.post",
|
|
200
|
+
recoverable: true,
|
|
201
|
+
scopeTargetOf: (p) => ({ kind: "channel", value: `${p.teamId}/${p.channelId}` }),
|
|
202
|
+
},
|
|
203
|
+
"Post a message to a team channel.",
|
|
204
|
+
teamsMessagePostSchema,
|
|
205
|
+
async (parsed) => {
|
|
206
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
207
|
+
const tid = encodeURIComponent(parsed.teamId);
|
|
208
|
+
const cid = encodeURIComponent(parsed.channelId);
|
|
209
|
+
const path = `/teams/${tid}/channels/${cid}/messages`;
|
|
210
|
+
const r = await postGraphMessage(token, path, parsed.body, parsed.contentType);
|
|
211
|
+
return graphListResult(r);
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const teamsMessagePostChatSchema = z.object({
|
|
216
|
+
chatId: z.string().min(1),
|
|
217
|
+
body: z.string().min(1),
|
|
218
|
+
contentType: z.enum(["text", "html"]).optional(),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// A WRITE: it posts a message. It was registered as a read, which in standalone mode offered it
|
|
222
|
+
// to every client with no consent prompt, scope check, budget or audit record.
|
|
223
|
+
registerWriteTool(
|
|
224
|
+
"teams_message_post_chat",
|
|
225
|
+
{
|
|
226
|
+
mutates: "teams.message.postChat",
|
|
227
|
+
recoverable: true,
|
|
228
|
+
scopeTargetOf: (p) => ({ kind: "chat", value: p.chatId }),
|
|
229
|
+
},
|
|
230
|
+
"Post a message to a chat.",
|
|
231
|
+
teamsMessagePostChatSchema,
|
|
232
|
+
async (parsed) => {
|
|
233
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
234
|
+
const id = encodeURIComponent(parsed.chatId);
|
|
235
|
+
const r = await postGraphMessage(
|
|
236
|
+
token,
|
|
237
|
+
`/chats/${id}/messages`,
|
|
238
|
+
parsed.body,
|
|
239
|
+
parsed.contentType,
|
|
240
|
+
);
|
|
241
|
+
return graphListResult(r);
|
|
242
|
+
},
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// --- ChatOps operational tools (Slice 5) -------------------------------------------------------
|
|
246
|
+
// `teams_chat_post` uses bot app credentials (Bot Framework), distinct from the user Graph token.
|
|
247
|
+
// Only `chatops/reply-dispatcher.ts` + `chatops/transport/` reference `teams_chat_post` (static D17).
|
|
248
|
+
|
|
249
|
+
const teamsUserInfoSchema = z.object({ userId: z.string().min(1) });
|
|
250
|
+
reg(
|
|
251
|
+
"teams_user_info",
|
|
252
|
+
"Fetch a Teams/AAD user (incl. mail/userPrincipalName) by id (ChatOps identity mapping).",
|
|
253
|
+
teamsUserInfoSchema,
|
|
254
|
+
async (parsed) => {
|
|
255
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
256
|
+
const r = await teamsPagedGraph(
|
|
257
|
+
token,
|
|
258
|
+
undefined,
|
|
259
|
+
`/users/${encodeURIComponent(parsed.userId)}`,
|
|
260
|
+
);
|
|
261
|
+
return graphListResult(r);
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const teamsChatPostSchema = z.object({
|
|
266
|
+
conversationId: z.string().min(1),
|
|
267
|
+
text: z.string().min(1),
|
|
268
|
+
});
|
|
269
|
+
registerWriteTool(
|
|
270
|
+
"teams_chat_post",
|
|
271
|
+
{
|
|
272
|
+
mutates: "teams.chat.post",
|
|
273
|
+
recoverable: true,
|
|
274
|
+
scopeTargetOf: (p) => ({ kind: "chat", value: p.conversationId }),
|
|
275
|
+
},
|
|
276
|
+
"Post an operational bot message to a Teams conversation (ChatOps reply surface; bot app creds).",
|
|
277
|
+
teamsChatPostSchema,
|
|
278
|
+
async (parsed) => {
|
|
279
|
+
const r = await teamsBotSendActivity(parsed.conversationId, parsed.text);
|
|
280
|
+
return graphListResult(r);
|
|
281
|
+
},
|
|
282
|
+
);
|
|
283
|
+
}
|
|
@@ -1,74 +1,6 @@
|
|
|
1
|
-
import { signAppStoreConnectJwt } from "@nimbus-dev/sdk";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { matchesResult } from "../../../shared/mcp-search-tool.ts";
|
|
4
|
-
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
5
1
|
import { runReadOnlyMcpConnector } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
6
|
-
import {
|
|
7
|
-
import { filterTestflightBuilds } from "./search-filter.ts";
|
|
8
|
-
|
|
9
|
-
const APPSTORECONNECT_API = "https://api.appstoreconnect.apple.com";
|
|
10
|
-
|
|
11
|
-
function authHeader(): Record<string, string> {
|
|
12
|
-
const token = signAppStoreConnectJwt(jwtParamsFromEnv());
|
|
13
|
-
return { Authorization: `Bearer ${token}`, Accept: "application/json" };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function appstoreGet(path: string): Promise<unknown> {
|
|
17
|
-
const res = await fetch(`${APPSTORECONNECT_API}${path}`, { headers: authHeader() });
|
|
18
|
-
const text = await res.text();
|
|
19
|
-
if (!res.ok) {
|
|
20
|
-
throw new Error(`App Store Connect ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
21
|
-
}
|
|
22
|
-
return JSON.parse(text) as unknown;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function buildsPath(appId: string, limit: number): string {
|
|
26
|
-
return `/v1/builds?filter[app]=${encodeURIComponent(appId)}&sort=-uploadedDate&limit=${String(limit)}`;
|
|
27
|
-
}
|
|
2
|
+
import { registerTestflightTools } from "./tools.ts";
|
|
28
3
|
|
|
29
4
|
await runReadOnlyMcpConnector("nimbus-testflight", (reg) => {
|
|
30
|
-
reg
|
|
31
|
-
"testflight_list",
|
|
32
|
-
"List TestFlight resources via the App Store Connect API. When `appId` is omitted, returns the user's accessible apps via `GET /v1/apps`. When `appId` is provided, returns that app's most-recent builds via `GET /v1/builds?filter[app]=<id>&sort=-uploadedDate` (limit optional).",
|
|
33
|
-
z.object({
|
|
34
|
-
appId: z.string().min(1).optional(),
|
|
35
|
-
limit: z.number().int().min(1).max(200).optional(),
|
|
36
|
-
}),
|
|
37
|
-
async (p) => {
|
|
38
|
-
if (p.appId === undefined) {
|
|
39
|
-
return jsonResult(await appstoreGet("/v1/apps"));
|
|
40
|
-
}
|
|
41
|
-
return jsonResult(await appstoreGet(buildsPath(p.appId, p.limit ?? 50)));
|
|
42
|
-
},
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
reg(
|
|
46
|
-
"testflight_get",
|
|
47
|
-
"Fetch a single TestFlight resource. When `buildId` is provided, returns the build via `GET /v1/builds/<buildId>`. When omitted, returns the app via `GET /v1/apps/<appId>`.",
|
|
48
|
-
z.object({
|
|
49
|
-
appId: z.string().min(1),
|
|
50
|
-
buildId: z.string().min(1).optional(),
|
|
51
|
-
}),
|
|
52
|
-
async (p) => {
|
|
53
|
-
if (p.buildId === undefined) {
|
|
54
|
-
return jsonResult(await appstoreGet(`/v1/apps/${encodeURIComponent(p.appId)}`));
|
|
55
|
-
}
|
|
56
|
-
return jsonResult(await appstoreGet(`/v1/builds/${encodeURIComponent(p.buildId)}`));
|
|
57
|
-
},
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
reg(
|
|
61
|
-
"testflight_search",
|
|
62
|
-
"Substring search across a TestFlight app's recent builds. Matches the query against each build's `version`, `processingState`, `minOsVersion`, and `id` (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
63
|
-
z.object({
|
|
64
|
-
appId: z.string().min(1),
|
|
65
|
-
query: z.string().min(1),
|
|
66
|
-
limit: z.number().int().min(1).max(200).optional(),
|
|
67
|
-
}),
|
|
68
|
-
async (p) => {
|
|
69
|
-
const root = await appstoreGet(buildsPath(p.appId, 200));
|
|
70
|
-
const data = (root as { data?: unknown[] } | null)?.data;
|
|
71
|
-
return matchesResult(data, filterTestflightBuilds, p);
|
|
72
|
-
},
|
|
73
|
-
);
|
|
5
|
+
registerTestflightTools(reg);
|
|
74
6
|
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { signAppStoreConnectJwt } from "@nimbus-dev/sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createJsonGetter } from "../../../shared/env-json-api.ts";
|
|
4
|
+
import { matchesResult } from "../../../shared/mcp-search-tool.ts";
|
|
5
|
+
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
6
|
+
import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
7
|
+
import { jwtParamsFromEnv } from "./jwt.ts";
|
|
8
|
+
import { filterTestflightBuilds } from "./search-filter.ts";
|
|
9
|
+
|
|
10
|
+
const APPSTORECONNECT_API = "https://api.appstoreconnect.apple.com";
|
|
11
|
+
|
|
12
|
+
function authHeader(): Record<string, string> {
|
|
13
|
+
const token = signAppStoreConnectJwt(jwtParamsFromEnv());
|
|
14
|
+
return { Authorization: `Bearer ${token}`, Accept: "application/json" };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const appstoreGet = createJsonGetter({
|
|
18
|
+
base: APPSTORECONNECT_API,
|
|
19
|
+
label: "App Store Connect",
|
|
20
|
+
headers: authHeader,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function buildsPath(appId: string, limit: number): string {
|
|
24
|
+
return `/v1/builds?filter[app]=${encodeURIComponent(appId)}&sort=-uploadedDate&limit=${String(limit)}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
28
|
+
export const TESTFLIGHT_TOOL_NAMES = [
|
|
29
|
+
"testflight_list",
|
|
30
|
+
"testflight_get",
|
|
31
|
+
"testflight_search",
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
export function registerTestflightTools(reg: ZodToolRegistrar): void {
|
|
35
|
+
reg(
|
|
36
|
+
"testflight_list",
|
|
37
|
+
"List TestFlight resources via the App Store Connect API. When `appId` is omitted, returns the user's accessible apps via `GET /v1/apps`. When `appId` is provided, returns that app's most-recent builds via `GET /v1/builds?filter[app]=<id>&sort=-uploadedDate` (limit optional).",
|
|
38
|
+
z.object({
|
|
39
|
+
appId: z.string().min(1).optional(),
|
|
40
|
+
limit: z.number().int().min(1).max(200).optional(),
|
|
41
|
+
}),
|
|
42
|
+
async (p) => {
|
|
43
|
+
if (p.appId === undefined) {
|
|
44
|
+
return jsonResult(await appstoreGet("/v1/apps"));
|
|
45
|
+
}
|
|
46
|
+
return jsonResult(await appstoreGet(buildsPath(p.appId, p.limit ?? 50)));
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
reg(
|
|
51
|
+
"testflight_get",
|
|
52
|
+
"Fetch a single TestFlight resource. When `buildId` is provided, returns the build via `GET /v1/builds/<buildId>`. When omitted, returns the app via `GET /v1/apps/<appId>`.",
|
|
53
|
+
z.object({
|
|
54
|
+
appId: z.string().min(1),
|
|
55
|
+
buildId: z.string().min(1).optional(),
|
|
56
|
+
}),
|
|
57
|
+
async (p) => {
|
|
58
|
+
if (p.buildId === undefined) {
|
|
59
|
+
return jsonResult(await appstoreGet(`/v1/apps/${encodeURIComponent(p.appId)}`));
|
|
60
|
+
}
|
|
61
|
+
return jsonResult(await appstoreGet(`/v1/builds/${encodeURIComponent(p.buildId)}`));
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
reg(
|
|
66
|
+
"testflight_search",
|
|
67
|
+
"Substring search across a TestFlight app's recent builds. Matches the query against each build's `version`, `processingState`, `minOsVersion`, and `id` (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
68
|
+
z.object({
|
|
69
|
+
appId: z.string().min(1),
|
|
70
|
+
query: z.string().min(1),
|
|
71
|
+
limit: z.number().int().min(1).max(200).optional(),
|
|
72
|
+
}),
|
|
73
|
+
async (p) => {
|
|
74
|
+
const root = await appstoreGet(buildsPath(p.appId, 200));
|
|
75
|
+
const data = (root as { data?: unknown[] } | null)?.data;
|
|
76
|
+
return matchesResult(data, filterTestflightBuilds, p);
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
asObjectish,
|
|
3
3
|
makeQueryFilter,
|
|
4
|
+
nestedString,
|
|
4
5
|
type SearchMatchOptions,
|
|
5
6
|
stringField,
|
|
6
7
|
} from "../../../shared/search-filter.ts";
|
|
7
8
|
|
|
8
9
|
export type VercelSearchMatchOptions = SearchMatchOptions;
|
|
9
10
|
|
|
10
|
-
function commitMessage(row: Record<string, unknown>): string {
|
|
11
|
-
const meta = row["meta"];
|
|
12
|
-
if (meta === null || typeof meta !== "object" || Array.isArray(meta)) {
|
|
13
|
-
return "";
|
|
14
|
-
}
|
|
15
|
-
const v = (meta as Record<string, unknown>)["githubCommitMessage"];
|
|
16
|
-
return typeof v === "string" ? v : "";
|
|
17
|
-
}
|
|
18
|
-
|
|
19
11
|
function fieldsOf(item: unknown): readonly string[] | null {
|
|
20
12
|
const row = asObjectish(item);
|
|
21
13
|
if (row === undefined) {
|
|
@@ -27,7 +19,7 @@ function fieldsOf(item: unknown): readonly string[] | null {
|
|
|
27
19
|
stringField(row, "state"),
|
|
28
20
|
stringField(row, "target"),
|
|
29
21
|
stringField(row, "url"),
|
|
30
|
-
|
|
22
|
+
nestedString(row, ["meta", "githubCommitMessage"]),
|
|
31
23
|
];
|
|
32
24
|
}
|
|
33
25
|
|
|
@@ -1,79 +1,6 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { matchesResult, searchToolInputSchema } from "../../../shared/mcp-search-tool.ts";
|
|
3
|
-
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
4
1
|
import { runReadOnlyMcpConnector } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const BASE = "https://api.vercel.com";
|
|
8
|
-
|
|
9
|
-
function token(): string {
|
|
10
|
-
const t = process.env["VERCEL_TOKEN"]?.trim();
|
|
11
|
-
if (t === undefined || t === "") {
|
|
12
|
-
throw new Error("VERCEL_TOKEN is not set");
|
|
13
|
-
}
|
|
14
|
-
return t;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function teamId(): string | undefined {
|
|
18
|
-
const v = process.env["VERCEL_TEAM_ID"]?.trim();
|
|
19
|
-
return v === undefined || v === "" ? undefined : v;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function authHeader(): Record<string, string> {
|
|
23
|
-
return { Authorization: `Bearer ${token()}`, Accept: "application/json" };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function withTeam(path: string): string {
|
|
27
|
-
const team = teamId();
|
|
28
|
-
if (team === undefined) {
|
|
29
|
-
return path;
|
|
30
|
-
}
|
|
31
|
-
const sep = path.includes("?") ? "&" : "?";
|
|
32
|
-
return `${path}${sep}teamId=${encodeURIComponent(team)}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function vercelGet(path: string): Promise<unknown> {
|
|
36
|
-
const res = await fetch(`${BASE}${withTeam(path)}`, { headers: authHeader() });
|
|
37
|
-
const text = await res.text();
|
|
38
|
-
if (!res.ok) {
|
|
39
|
-
throw new Error(`Vercel ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
40
|
-
}
|
|
41
|
-
return JSON.parse(text) as unknown;
|
|
42
|
-
}
|
|
2
|
+
import { registerVercelTools } from "./tools.ts";
|
|
43
3
|
|
|
44
4
|
await runReadOnlyMcpConnector("nimbus-vercel", (reg) => {
|
|
45
|
-
reg
|
|
46
|
-
"vercel_list",
|
|
47
|
-
"List recent Vercel deployments (`GET /v6/deployments`), most-recent-first, capped at `limit` (default 100). Returns the `{ deployments, pagination }` envelope.",
|
|
48
|
-
z.object({
|
|
49
|
-
limit: z.number().int().min(1).max(100).optional(),
|
|
50
|
-
}),
|
|
51
|
-
async (p) => {
|
|
52
|
-
const search = new URLSearchParams({ limit: String(p.limit ?? 100) });
|
|
53
|
-
return jsonResult(await vercelGet(`/v6/deployments?${search.toString()}`));
|
|
54
|
-
},
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
reg(
|
|
58
|
-
"vercel_get",
|
|
59
|
-
"Fetch one Vercel deployment by its id or *.vercel.app URL (`GET /v13/deployments/{idOrUrl}`). Returns the deployment object directly. Throws when no match is found.",
|
|
60
|
-
z.object({
|
|
61
|
-
idOrUrl: z.string().min(1),
|
|
62
|
-
}),
|
|
63
|
-
async (p) => {
|
|
64
|
-
return jsonResult(await vercelGet(`/v13/deployments/${encodeURIComponent(p.idOrUrl)}`));
|
|
65
|
-
},
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
reg(
|
|
69
|
-
"vercel_search",
|
|
70
|
-
"Substring search across recent Vercel deployments. Matches the query against uid, project name, state, target, the *.vercel.app host, and the git commit message (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
71
|
-
searchToolInputSchema(100),
|
|
72
|
-
async (p) => {
|
|
73
|
-
const search = new URLSearchParams({ limit: "100" });
|
|
74
|
-
const root = await vercelGet(`/v6/deployments?${search.toString()}`);
|
|
75
|
-
const deployments = (root as { deployments?: unknown[] } | null)?.deployments;
|
|
76
|
-
return matchesResult(deployments, filterVercelDeployments, p);
|
|
77
|
-
},
|
|
78
|
-
);
|
|
5
|
+
registerVercelTools(reg);
|
|
79
6
|
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createJsonGetter, envAuthHeaders } from "../../../shared/env-json-api.ts";
|
|
3
|
+
import { matchesResult, searchToolInputSchema } from "../../../shared/mcp-search-tool.ts";
|
|
4
|
+
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
5
|
+
import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
6
|
+
import { filterVercelDeployments } from "./search-filter.ts";
|
|
7
|
+
|
|
8
|
+
const BASE = "https://api.vercel.com";
|
|
9
|
+
|
|
10
|
+
function teamId(): string | undefined {
|
|
11
|
+
const v = process.env["VERCEL_TEAM_ID"]?.trim();
|
|
12
|
+
return v === undefined || v === "" ? undefined : v;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function withTeam(path: string): string {
|
|
16
|
+
const team = teamId();
|
|
17
|
+
if (team === undefined) {
|
|
18
|
+
return path;
|
|
19
|
+
}
|
|
20
|
+
const sep = path.includes("?") ? "&" : "?";
|
|
21
|
+
return `${path}${sep}teamId=${encodeURIComponent(team)}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const getJson = createJsonGetter({
|
|
25
|
+
base: BASE,
|
|
26
|
+
label: "Vercel",
|
|
27
|
+
headers: envAuthHeaders({ env: "VERCEL_TOKEN" }),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/** Every request is scoped to `VERCEL_TEAM_ID` when one is configured. */
|
|
31
|
+
async function vercelGet(path: string): Promise<unknown> {
|
|
32
|
+
return getJson(withTeam(path));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
36
|
+
export const VERCEL_TOOL_NAMES = ["vercel_get", "vercel_list", "vercel_search"] as const;
|
|
37
|
+
|
|
38
|
+
export function registerVercelTools(reg: ZodToolRegistrar): void {
|
|
39
|
+
reg(
|
|
40
|
+
"vercel_list",
|
|
41
|
+
"List recent Vercel deployments (`GET /v6/deployments`), most-recent-first, capped at `limit` (default 100). Returns the `{ deployments, pagination }` envelope.",
|
|
42
|
+
z.object({
|
|
43
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
44
|
+
}),
|
|
45
|
+
async (p) => {
|
|
46
|
+
const search = new URLSearchParams({ limit: String(p.limit ?? 100) });
|
|
47
|
+
return jsonResult(await vercelGet(`/v6/deployments?${search.toString()}`));
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
reg(
|
|
52
|
+
"vercel_get",
|
|
53
|
+
"Fetch one Vercel deployment by its id or *.vercel.app URL (`GET /v13/deployments/{idOrUrl}`). Returns the deployment object directly. Throws when no match is found.",
|
|
54
|
+
z.object({
|
|
55
|
+
idOrUrl: z.string().min(1),
|
|
56
|
+
}),
|
|
57
|
+
async (p) => {
|
|
58
|
+
return jsonResult(await vercelGet(`/v13/deployments/${encodeURIComponent(p.idOrUrl)}`));
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
reg(
|
|
63
|
+
"vercel_search",
|
|
64
|
+
"Substring search across recent Vercel deployments. Matches the query against uid, project name, state, target, the *.vercel.app host, and the git commit message (case-insensitive). Returns a `{ matches: [...] }` envelope.",
|
|
65
|
+
searchToolInputSchema(100),
|
|
66
|
+
async (p) => {
|
|
67
|
+
const search = new URLSearchParams({ limit: "100" });
|
|
68
|
+
const root = await vercelGet(`/v6/deployments?${search.toString()}`);
|
|
69
|
+
const deployments = (root as { deployments?: unknown[] } | null)?.deployments;
|
|
70
|
+
return matchesResult(deployments, filterVercelDeployments, p);
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
}
|