workflowskill 0.1.0

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.
@@ -0,0 +1,209 @@
1
+ import { gmail } from "@googleapis/gmail";
2
+
3
+ //#region src/dev-tools/tools/gmail.ts
4
+ const searchDescriptor = {
5
+ name: "gmail.search",
6
+ description: "Search Gmail messages matching a query.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ query: {
11
+ type: "string",
12
+ description: "Gmail search query (same syntax as Gmail search bar)"
13
+ },
14
+ max_results: {
15
+ type: "number",
16
+ description: "Maximum messages to return (default: 10)"
17
+ }
18
+ },
19
+ required: ["query"]
20
+ },
21
+ outputSchema: {
22
+ type: "object",
23
+ properties: { messages: {
24
+ type: "array",
25
+ items: {
26
+ type: "object",
27
+ properties: {
28
+ id: { type: "string" },
29
+ from: { type: "string" },
30
+ to: { type: "string" },
31
+ subject: { type: "string" },
32
+ snippet: { type: "string" },
33
+ date: { type: "string" }
34
+ }
35
+ }
36
+ } }
37
+ }
38
+ };
39
+ const readDescriptor = {
40
+ name: "gmail.read",
41
+ description: "Read a full Gmail message by ID.",
42
+ inputSchema: {
43
+ type: "object",
44
+ properties: { message_id: {
45
+ type: "string",
46
+ description: "Gmail message ID"
47
+ } },
48
+ required: ["message_id"]
49
+ },
50
+ outputSchema: {
51
+ type: "object",
52
+ properties: {
53
+ id: { type: "string" },
54
+ from: { type: "string" },
55
+ to: { type: "string" },
56
+ subject: { type: "string" },
57
+ body: { type: "string" },
58
+ date: { type: "string" }
59
+ }
60
+ }
61
+ };
62
+ const sendDescriptor = {
63
+ name: "gmail.send",
64
+ description: "Send an email via Gmail.",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: {
68
+ to: {
69
+ type: "string",
70
+ description: "Recipient email address"
71
+ },
72
+ subject: {
73
+ type: "string",
74
+ description: "Email subject"
75
+ },
76
+ body: {
77
+ type: "string",
78
+ description: "Email body (plain text)"
79
+ }
80
+ },
81
+ required: [
82
+ "to",
83
+ "subject",
84
+ "body"
85
+ ]
86
+ },
87
+ outputSchema: {
88
+ type: "object",
89
+ properties: { message_id: { type: "string" } }
90
+ }
91
+ };
92
+ function getHeader(headers, name) {
93
+ return headers?.find((h) => h.name?.toLowerCase() === name.toLowerCase())?.value ?? "";
94
+ }
95
+ function decodeBody(message) {
96
+ const parts = message.payload?.parts;
97
+ if (parts) {
98
+ const textPart = parts.find((p) => p.mimeType === "text/plain");
99
+ if (textPart?.body?.data) return Buffer.from(textPart.body.data, "base64url").toString("utf-8");
100
+ }
101
+ if (message.payload?.body?.data) return Buffer.from(message.payload.body.data, "base64url").toString("utf-8");
102
+ return message.snippet ?? "";
103
+ }
104
+ function createHandlers(auth) {
105
+ const client = gmail({
106
+ version: "v1",
107
+ auth
108
+ });
109
+ async function search(args) {
110
+ const query = args.query;
111
+ const maxResults = args.max_results ?? 10;
112
+ if (!query || typeof query !== "string") return {
113
+ output: null,
114
+ error: "gmail.search: \"query\" is required"
115
+ };
116
+ try {
117
+ const messageIds = (await client.users.messages.list({
118
+ userId: "me",
119
+ q: query,
120
+ maxResults
121
+ })).data.messages ?? [];
122
+ return { output: { messages: await Promise.all(messageIds.map(async (m) => {
123
+ const msg = await client.users.messages.get({
124
+ userId: "me",
125
+ id: m.id,
126
+ format: "metadata",
127
+ metadataHeaders: [
128
+ "From",
129
+ "To",
130
+ "Subject",
131
+ "Date"
132
+ ]
133
+ });
134
+ const headers = msg.data.payload?.headers;
135
+ return {
136
+ id: msg.data.id ?? "",
137
+ from: getHeader(headers, "From"),
138
+ to: getHeader(headers, "To"),
139
+ subject: getHeader(headers, "Subject"),
140
+ snippet: msg.data.snippet ?? "",
141
+ date: getHeader(headers, "Date")
142
+ };
143
+ })) } };
144
+ } catch (err) {
145
+ return {
146
+ output: null,
147
+ error: `gmail.search failed: ${err instanceof Error ? err.message : String(err)}`
148
+ };
149
+ }
150
+ }
151
+ async function read(args) {
152
+ const messageId = args.message_id;
153
+ if (!messageId || typeof messageId !== "string") return {
154
+ output: null,
155
+ error: "gmail.read: \"message_id\" is required"
156
+ };
157
+ try {
158
+ const msg = await client.users.messages.get({
159
+ userId: "me",
160
+ id: messageId,
161
+ format: "full"
162
+ });
163
+ const headers = msg.data.payload?.headers;
164
+ return { output: {
165
+ id: msg.data.id ?? "",
166
+ from: getHeader(headers, "From"),
167
+ to: getHeader(headers, "To"),
168
+ subject: getHeader(headers, "Subject"),
169
+ body: decodeBody(msg.data),
170
+ date: getHeader(headers, "Date")
171
+ } };
172
+ } catch (err) {
173
+ return {
174
+ output: null,
175
+ error: `gmail.read failed: ${err instanceof Error ? err.message : String(err)}`
176
+ };
177
+ }
178
+ }
179
+ async function send(args) {
180
+ const to = args.to;
181
+ const subject = args.subject;
182
+ const body = args.body;
183
+ if (!to || !subject || !body) return {
184
+ output: null,
185
+ error: "gmail.send: \"to\", \"subject\", and \"body\" are required"
186
+ };
187
+ try {
188
+ const raw = Buffer.from(`To: ${to}\r\nSubject: ${subject}\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n${body}`).toString("base64url");
189
+ return { output: { message_id: (await client.users.messages.send({
190
+ userId: "me",
191
+ requestBody: { raw }
192
+ })).data.id ?? "" } };
193
+ } catch (err) {
194
+ return {
195
+ output: null,
196
+ error: `gmail.send failed: ${err instanceof Error ? err.message : String(err)}`
197
+ };
198
+ }
199
+ }
200
+ return {
201
+ search,
202
+ read,
203
+ send
204
+ };
205
+ }
206
+
207
+ //#endregion
208
+ export { createHandlers, readDescriptor, searchDescriptor, sendDescriptor };
209
+ //# sourceMappingURL=gmail-CYBJ6Dxa.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail-CYBJ6Dxa.mjs","names":[],"sources":["../src/dev-tools/tools/gmail.ts"],"sourcesContent":["// Tools: gmail.search, gmail.read, gmail.send — Gmail integration via googleapis.\n\nimport { gmail, gmail_v1 } from '@googleapis/gmail';\nimport type { OAuth2Client } from 'google-auth-library';\nimport type { ToolDescriptor, ToolResult } from '../../types/index.js';\n\n// ─── Tool descriptors ────────────────────────────────────────────────────────\n\nexport const searchDescriptor: ToolDescriptor = {\n name: 'gmail.search',\n description: 'Search Gmail messages matching a query.',\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Gmail search query (same syntax as Gmail search bar)' },\n max_results: { type: 'number', description: 'Maximum messages to return (default: 10)' },\n },\n required: ['query'],\n },\n outputSchema: {\n type: 'object',\n properties: {\n messages: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n from: { type: 'string' },\n to: { type: 'string' },\n subject: { type: 'string' },\n snippet: { type: 'string' },\n date: { type: 'string' },\n },\n },\n },\n },\n },\n};\n\nexport const readDescriptor: ToolDescriptor = {\n name: 'gmail.read',\n description: 'Read a full Gmail message by ID.',\n inputSchema: {\n type: 'object',\n properties: {\n message_id: { type: 'string', description: 'Gmail message ID' },\n },\n required: ['message_id'],\n },\n outputSchema: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n from: { type: 'string' },\n to: { type: 'string' },\n subject: { type: 'string' },\n body: { type: 'string' },\n date: { type: 'string' },\n },\n },\n};\n\nexport const sendDescriptor: ToolDescriptor = {\n name: 'gmail.send',\n description: 'Send an email via Gmail.',\n inputSchema: {\n type: 'object',\n properties: {\n to: { type: 'string', description: 'Recipient email address' },\n subject: { type: 'string', description: 'Email subject' },\n body: { type: 'string', description: 'Email body (plain text)' },\n },\n required: ['to', 'subject', 'body'],\n },\n outputSchema: {\n type: 'object',\n properties: {\n message_id: { type: 'string' },\n },\n },\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction getHeader(\n headers: gmail_v1.Schema$MessagePartHeader[] | undefined,\n name: string,\n): string {\n return headers?.find((h) => h.name?.toLowerCase() === name.toLowerCase())?.value ?? '';\n}\n\nfunction decodeBody(message: gmail_v1.Schema$Message): string {\n // Try to get plain text body from parts\n const parts = message.payload?.parts;\n if (parts) {\n const textPart = parts.find((p) => p.mimeType === 'text/plain');\n if (textPart?.body?.data) {\n return Buffer.from(textPart.body.data, 'base64url').toString('utf-8');\n }\n }\n // Fallback to payload body\n if (message.payload?.body?.data) {\n return Buffer.from(message.payload.body.data, 'base64url').toString('utf-8');\n }\n return message.snippet ?? '';\n}\n\n// ─── Handlers ────────────────────────────────────────────────────────────────\n\nexport function createHandlers(auth: OAuth2Client) {\n const client = gmail({ version: 'v1', auth });\n\n async function search(args: Record<string, unknown>): Promise<ToolResult> {\n const query = args.query as string | undefined;\n const maxResults = (args.max_results as number | undefined) ?? 10;\n\n if (!query || typeof query !== 'string') {\n return { output: null, error: 'gmail.search: \"query\" is required' };\n }\n\n try {\n const listRes = await client.users.messages.list({\n userId: 'me',\n q: query,\n maxResults,\n });\n\n const messageIds = listRes.data.messages ?? [];\n const messages = await Promise.all(\n messageIds.map(async (m: gmail_v1.Schema$Message) => {\n const msg = await client.users.messages.get({\n userId: 'me',\n id: m.id!,\n format: 'metadata',\n metadataHeaders: ['From', 'To', 'Subject', 'Date'],\n });\n const headers = msg.data.payload?.headers;\n return {\n id: msg.data.id ?? '',\n from: getHeader(headers, 'From'),\n to: getHeader(headers, 'To'),\n subject: getHeader(headers, 'Subject'),\n snippet: msg.data.snippet ?? '',\n date: getHeader(headers, 'Date'),\n };\n }),\n );\n\n return { output: { messages } };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { output: null, error: `gmail.search failed: ${message}` };\n }\n }\n\n async function read(args: Record<string, unknown>): Promise<ToolResult> {\n const messageId = args.message_id as string | undefined;\n\n if (!messageId || typeof messageId !== 'string') {\n return { output: null, error: 'gmail.read: \"message_id\" is required' };\n }\n\n try {\n const msg = await client.users.messages.get({\n userId: 'me',\n id: messageId,\n format: 'full',\n });\n\n const headers = msg.data.payload?.headers;\n return {\n output: {\n id: msg.data.id ?? '',\n from: getHeader(headers, 'From'),\n to: getHeader(headers, 'To'),\n subject: getHeader(headers, 'Subject'),\n body: decodeBody(msg.data),\n date: getHeader(headers, 'Date'),\n },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { output: null, error: `gmail.read failed: ${message}` };\n }\n }\n\n async function send(args: Record<string, unknown>): Promise<ToolResult> {\n const to = args.to as string | undefined;\n const subject = args.subject as string | undefined;\n const body = args.body as string | undefined;\n\n if (!to || !subject || !body) {\n return { output: null, error: 'gmail.send: \"to\", \"subject\", and \"body\" are required' };\n }\n\n try {\n const raw = Buffer.from(\n `To: ${to}\\r\\nSubject: ${subject}\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\\r\\n${body}`,\n ).toString('base64url');\n\n const res = await client.users.messages.send({\n userId: 'me',\n requestBody: { raw },\n });\n\n return { output: { message_id: res.data.id ?? '' } };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { output: null, error: `gmail.send failed: ${message}` };\n }\n }\n\n return { search, read, send };\n}\n"],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import * as cheerio from "cheerio";
2
+
3
+ //#region src/dev-tools/tools/html-select.ts
4
+ const descriptor = {
5
+ name: "html.select",
6
+ description: "Extract data from HTML using CSS selectors. Returns text content, attribute values, or structured objects.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ html: {
11
+ type: "string",
12
+ description: "HTML content to search"
13
+ },
14
+ selector: {
15
+ type: "string",
16
+ description: "CSS selector to match elements"
17
+ },
18
+ attribute: {
19
+ type: "string",
20
+ description: "Attribute to extract instead of text content (ignored when fields is present)"
21
+ },
22
+ limit: {
23
+ type: "number",
24
+ description: "Maximum number of results to return"
25
+ },
26
+ fields: {
27
+ type: "object",
28
+ description: "Map of field names to sub-selectors for structured extraction. Each value is a CSS selector scoped to the matched parent. Append \" @attr\" to extract an attribute (e.g. \"a @href\", \"img @src\"). Bare \"@attr\" extracts from the parent itself. When present, returns array of objects instead of strings."
29
+ }
30
+ },
31
+ required: ["html", "selector"]
32
+ },
33
+ outputSchema: {
34
+ type: "object",
35
+ properties: { results: {
36
+ type: "array",
37
+ description: "Extracted values: strings without fields, objects with fields"
38
+ } }
39
+ }
40
+ };
41
+ /**
42
+ * Parse a field spec string into a sub-selector and optional attribute name.
43
+ * Formats: "h3.title" → text, "a.link @href" → attribute, "@data-id" → parent attribute.
44
+ */
45
+ function parseFieldSpec(spec) {
46
+ const atIndex = spec.lastIndexOf(" @");
47
+ if (atIndex >= 0) return {
48
+ subSelector: spec.slice(0, atIndex).trim() || null,
49
+ attr: spec.slice(atIndex + 2).trim() || null
50
+ };
51
+ if (spec.startsWith("@")) return {
52
+ subSelector: null,
53
+ attr: spec.slice(1)
54
+ };
55
+ return {
56
+ subSelector: spec,
57
+ attr: null
58
+ };
59
+ }
60
+ async function handler(args) {
61
+ const { html, selector, attribute, limit, fields } = args;
62
+ if (!html || typeof html !== "string") return {
63
+ output: null,
64
+ error: "html.select: \"html\" is required and must be a string"
65
+ };
66
+ if (!selector || typeof selector !== "string") return {
67
+ output: null,
68
+ error: "html.select: \"selector\" is required and must be a string"
69
+ };
70
+ const $ = cheerio.load(html);
71
+ const elements = $(selector);
72
+ if (fields && typeof fields === "object") {
73
+ const parsedFields = Object.entries(fields).map(([name, spec]) => ({
74
+ name,
75
+ ...parseFieldSpec(spec)
76
+ }));
77
+ let results = [];
78
+ elements.each((_i, el) => {
79
+ const row = {};
80
+ for (const { name, subSelector, attr } of parsedFields) {
81
+ const target = subSelector ? $(el).find(subSelector).first() : $(el);
82
+ if (target.length === 0) row[name] = null;
83
+ else if (attr) row[name] = target.attr(attr) ?? null;
84
+ else row[name] = target.text().trim();
85
+ }
86
+ results.push(row);
87
+ });
88
+ if (limit !== void 0 && limit > 0) results = results.slice(0, limit);
89
+ return { output: { results } };
90
+ }
91
+ let results = [];
92
+ elements.each((_i, el) => {
93
+ if (attribute) {
94
+ const val = $(el).attr(attribute);
95
+ if (val !== void 0) results.push(val);
96
+ } else results.push($(el).text().trim());
97
+ });
98
+ if (limit !== void 0 && limit > 0) results = results.slice(0, limit);
99
+ return { output: { results } };
100
+ }
101
+
102
+ //#endregion
103
+ export { descriptor, handler };
104
+ //# sourceMappingURL=html-select-BZPYAr6k.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-select-BZPYAr6k.mjs","names":[],"sources":["../src/dev-tools/tools/html-select.ts"],"sourcesContent":["// Tool: html.select — extract data from HTML using CSS selectors.\n\nimport * as cheerio from 'cheerio';\nimport type { ToolDescriptor, ToolResult } from '../../types/index.js';\n\nexport interface HtmlSelectArgs {\n html: string;\n selector: string;\n attribute?: string;\n limit?: number;\n fields?: Record<string, string>;\n}\n\nexport const descriptor: ToolDescriptor = {\n name: 'html.select',\n description:\n 'Extract data from HTML using CSS selectors. Returns text content, attribute values, or structured objects.',\n inputSchema: {\n type: 'object',\n properties: {\n html: { type: 'string', description: 'HTML content to search' },\n selector: { type: 'string', description: 'CSS selector to match elements' },\n attribute: {\n type: 'string',\n description: 'Attribute to extract instead of text content (ignored when fields is present)',\n },\n limit: { type: 'number', description: 'Maximum number of results to return' },\n fields: {\n type: 'object',\n description:\n 'Map of field names to sub-selectors for structured extraction. Each value is a CSS selector scoped to the matched parent. Append \" @attr\" to extract an attribute (e.g. \"a @href\", \"img @src\"). Bare \"@attr\" extracts from the parent itself. When present, returns array of objects instead of strings.',\n },\n },\n required: ['html', 'selector'],\n },\n outputSchema: {\n type: 'object',\n properties: {\n results: {\n type: 'array',\n description: 'Extracted values: strings without fields, objects with fields',\n },\n },\n },\n};\n\n/**\n * Parse a field spec string into a sub-selector and optional attribute name.\n * Formats: \"h3.title\" → text, \"a.link @href\" → attribute, \"@data-id\" → parent attribute.\n */\nfunction parseFieldSpec(spec: string): { subSelector: string | null; attr: string | null } {\n const atIndex = spec.lastIndexOf(' @');\n if (atIndex >= 0) {\n // \"sub-selector @attr\"\n const subSelector = spec.slice(0, atIndex).trim() || null;\n const attr = spec.slice(atIndex + 2).trim();\n return { subSelector, attr: attr || null };\n }\n if (spec.startsWith('@')) {\n // Bare \"@attr\" — extract attribute from parent\n return { subSelector: null, attr: spec.slice(1) };\n }\n // Plain sub-selector — extract text\n return { subSelector: spec, attr: null };\n}\n\nexport async function handler(args: Record<string, unknown>): Promise<ToolResult> {\n const { html, selector, attribute, limit, fields } = args as unknown as HtmlSelectArgs;\n\n if (!html || typeof html !== 'string') {\n return { output: null, error: 'html.select: \"html\" is required and must be a string' };\n }\n if (!selector || typeof selector !== 'string') {\n return { output: null, error: 'html.select: \"selector\" is required and must be a string' };\n }\n\n const $ = cheerio.load(html);\n const elements = $(selector);\n\n if (fields && typeof fields === 'object') {\n // Structured extraction: return array of objects\n const parsedFields = Object.entries(fields).map(([name, spec]) => ({\n name,\n ...parseFieldSpec(spec),\n }));\n\n let results: Array<Record<string, string | null>> = [];\n elements.each((_i, el) => {\n const row: Record<string, string | null> = {};\n for (const { name, subSelector, attr } of parsedFields) {\n const target = subSelector ? $(el).find(subSelector).first() : $(el);\n if (target.length === 0) {\n row[name] = null;\n } else if (attr) {\n row[name] = target.attr(attr) ?? null;\n } else {\n row[name] = target.text().trim();\n }\n }\n results.push(row);\n });\n\n if (limit !== undefined && limit > 0) {\n results = results.slice(0, limit);\n }\n\n return { output: { results } };\n }\n\n // Simple extraction: return array of strings (original behavior)\n let results: string[] = [];\n elements.each((_i, el) => {\n if (attribute) {\n const val = $(el).attr(attribute);\n if (val !== undefined) {\n results.push(val);\n }\n } else {\n results.push($(el).text().trim());\n }\n });\n\n if (limit !== undefined && limit > 0) {\n results = results.slice(0, limit);\n }\n\n return { output: { results } };\n}\n"],"mappings":";;;AAaA,MAAa,aAA6B;CACxC,MAAM;CACN,aACE;CACF,aAAa;EACX,MAAM;EACN,YAAY;GACV,MAAM;IAAE,MAAM;IAAU,aAAa;IAA0B;GAC/D,UAAU;IAAE,MAAM;IAAU,aAAa;IAAkC;GAC3E,WAAW;IACT,MAAM;IACN,aAAa;IACd;GACD,OAAO;IAAE,MAAM;IAAU,aAAa;IAAuC;GAC7E,QAAQ;IACN,MAAM;IACN,aACE;IACH;GACF;EACD,UAAU,CAAC,QAAQ,WAAW;EAC/B;CACD,cAAc;EACZ,MAAM;EACN,YAAY,EACV,SAAS;GACP,MAAM;GACN,aAAa;GACd,EACF;EACF;CACF;;;;;AAMD,SAAS,eAAe,MAAmE;CACzF,MAAM,UAAU,KAAK,YAAY,KAAK;AACtC,KAAI,WAAW,EAIb,QAAO;EAAE,aAFW,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI;EAE/B,MADT,KAAK,MAAM,UAAU,EAAE,CAAC,MAAM,IACP;EAAM;AAE5C,KAAI,KAAK,WAAW,IAAI,CAEtB,QAAO;EAAE,aAAa;EAAM,MAAM,KAAK,MAAM,EAAE;EAAE;AAGnD,QAAO;EAAE,aAAa;EAAM,MAAM;EAAM;;AAG1C,eAAsB,QAAQ,MAAoD;CAChF,MAAM,EAAE,MAAM,UAAU,WAAW,OAAO,WAAW;AAErD,KAAI,CAAC,QAAQ,OAAO,SAAS,SAC3B,QAAO;EAAE,QAAQ;EAAM,OAAO;EAAwD;AAExF,KAAI,CAAC,YAAY,OAAO,aAAa,SACnC,QAAO;EAAE,QAAQ;EAAM,OAAO;EAA4D;CAG5F,MAAM,IAAI,QAAQ,KAAK,KAAK;CAC5B,MAAM,WAAW,EAAE,SAAS;AAE5B,KAAI,UAAU,OAAO,WAAW,UAAU;EAExC,MAAM,eAAe,OAAO,QAAQ,OAAO,CAAC,KAAK,CAAC,MAAM,WAAW;GACjE;GACA,GAAG,eAAe,KAAK;GACxB,EAAE;EAEH,IAAI,UAAgD,EAAE;AACtD,WAAS,MAAM,IAAI,OAAO;GACxB,MAAM,MAAqC,EAAE;AAC7C,QAAK,MAAM,EAAE,MAAM,aAAa,UAAU,cAAc;IACtD,MAAM,SAAS,cAAc,EAAE,GAAG,CAAC,KAAK,YAAY,CAAC,OAAO,GAAG,EAAE,GAAG;AACpE,QAAI,OAAO,WAAW,EACpB,KAAI,QAAQ;aACH,KACT,KAAI,QAAQ,OAAO,KAAK,KAAK,IAAI;QAEjC,KAAI,QAAQ,OAAO,MAAM,CAAC,MAAM;;AAGpC,WAAQ,KAAK,IAAI;IACjB;AAEF,MAAI,UAAU,UAAa,QAAQ,EACjC,WAAU,QAAQ,MAAM,GAAG,MAAM;AAGnC,SAAO,EAAE,QAAQ,EAAE,SAAS,EAAE;;CAIhC,IAAI,UAAoB,EAAE;AAC1B,UAAS,MAAM,IAAI,OAAO;AACxB,MAAI,WAAW;GACb,MAAM,MAAM,EAAE,GAAG,CAAC,KAAK,UAAU;AACjC,OAAI,QAAQ,OACV,SAAQ,KAAK,IAAI;QAGnB,SAAQ,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;GAEnC;AAEF,KAAI,UAAU,UAAa,QAAQ,EACjC,WAAU,QAAQ,MAAM,GAAG,MAAM;AAGnC,QAAO,EAAE,QAAQ,EAAE,SAAS,EAAE"}
@@ -0,0 +1,91 @@
1
+ //#region src/dev-tools/tools/http-request.ts
2
+ const descriptor = {
3
+ name: "http.request",
4
+ description: "Make an HTTP request and return the response status, headers, and body.",
5
+ inputSchema: {
6
+ type: "object",
7
+ properties: {
8
+ url: {
9
+ type: "string",
10
+ description: "The URL to request"
11
+ },
12
+ method: {
13
+ type: "string",
14
+ description: "HTTP method (default: GET)"
15
+ },
16
+ headers: {
17
+ type: "object",
18
+ description: "Request headers as key-value pairs"
19
+ },
20
+ body: {
21
+ type: "string",
22
+ description: "Request body (for POST/PUT/PATCH)"
23
+ },
24
+ timeout: {
25
+ type: "number",
26
+ description: "Request timeout in milliseconds (default: 30000)"
27
+ }
28
+ },
29
+ required: ["url"]
30
+ },
31
+ outputSchema: {
32
+ type: "object",
33
+ properties: {
34
+ status: {
35
+ type: "number",
36
+ description: "HTTP status code"
37
+ },
38
+ headers: {
39
+ type: "object",
40
+ description: "Response headers"
41
+ },
42
+ body: {
43
+ type: "object",
44
+ description: "Response body — parsed object when content-type is application/json, raw string otherwise"
45
+ }
46
+ }
47
+ }
48
+ };
49
+ async function handler(args) {
50
+ const { url, method, headers, body: requestBody, timeout } = args;
51
+ if (!url || typeof url !== "string") return {
52
+ output: null,
53
+ error: "http.request: \"url\" is required and must be a string"
54
+ };
55
+ try {
56
+ const controller = new AbortController();
57
+ const timeoutMs = timeout ?? 3e4;
58
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
59
+ const response = await fetch(url, {
60
+ method: method ?? "GET",
61
+ headers,
62
+ body: requestBody,
63
+ signal: controller.signal
64
+ });
65
+ clearTimeout(timer);
66
+ const responseText = await response.text();
67
+ const responseHeaders = {};
68
+ response.headers.forEach((value, key) => {
69
+ responseHeaders[key] = value;
70
+ });
71
+ const contentType = response.headers.get("content-type") ?? "";
72
+ let responseBody = responseText;
73
+ if (contentType.includes("application/json")) try {
74
+ responseBody = JSON.parse(responseText);
75
+ } catch {}
76
+ return { output: {
77
+ status: response.status,
78
+ headers: responseHeaders,
79
+ body: responseBody
80
+ } };
81
+ } catch (err) {
82
+ return {
83
+ output: null,
84
+ error: `http.request failed: ${err instanceof Error ? err.message : String(err)}`
85
+ };
86
+ }
87
+ }
88
+
89
+ //#endregion
90
+ export { descriptor, handler };
91
+ //# sourceMappingURL=http-request-k9bp5joL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-request-k9bp5joL.mjs","names":[],"sources":["../src/dev-tools/tools/http-request.ts"],"sourcesContent":["// Tool: http.request — make HTTP requests using Node built-in fetch.\n\nimport type { ToolDescriptor, ToolResult } from '../../types/index.js';\n\nexport interface HttpRequestArgs {\n url: string;\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n timeout?: number;\n}\n\nexport const descriptor: ToolDescriptor = {\n name: 'http.request',\n description: 'Make an HTTP request and return the response status, headers, and body.',\n inputSchema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'The URL to request' },\n method: { type: 'string', description: 'HTTP method (default: GET)' },\n headers: { type: 'object', description: 'Request headers as key-value pairs' },\n body: { type: 'string', description: 'Request body (for POST/PUT/PATCH)' },\n timeout: { type: 'number', description: 'Request timeout in milliseconds (default: 30000)' },\n },\n required: ['url'],\n },\n outputSchema: {\n type: 'object',\n properties: {\n status: { type: 'number', description: 'HTTP status code' },\n headers: { type: 'object', description: 'Response headers' },\n body: {\n type: 'object',\n description:\n 'Response body — parsed object when content-type is application/json, raw string otherwise',\n },\n },\n },\n};\n\nexport async function handler(args: Record<string, unknown>): Promise<ToolResult> {\n const { url, method, headers, body: requestBody, timeout } = args as unknown as HttpRequestArgs;\n\n if (!url || typeof url !== 'string') {\n return { output: null, error: 'http.request: \"url\" is required and must be a string' };\n }\n\n try {\n const controller = new AbortController();\n const timeoutMs = timeout ?? 30000;\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const response = await fetch(url, {\n method: method ?? 'GET',\n headers: headers,\n body: requestBody,\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n const responseText = await response.text();\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n // Auto-parse JSON responses so workflows can access fields directly\n const contentType = response.headers.get('content-type') ?? '';\n let responseBody: unknown = responseText;\n if (contentType.includes('application/json')) {\n try {\n responseBody = JSON.parse(responseText);\n } catch {\n // Keep raw text if JSON parsing fails\n }\n }\n\n return {\n output: {\n status: response.status,\n headers: responseHeaders,\n body: responseBody,\n },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { output: null, error: `http.request failed: ${message}` };\n }\n}\n"],"mappings":";AAYA,MAAa,aAA6B;CACxC,MAAM;CACN,aAAa;CACb,aAAa;EACX,MAAM;EACN,YAAY;GACV,KAAK;IAAE,MAAM;IAAU,aAAa;IAAsB;GAC1D,QAAQ;IAAE,MAAM;IAAU,aAAa;IAA8B;GACrE,SAAS;IAAE,MAAM;IAAU,aAAa;IAAsC;GAC9E,MAAM;IAAE,MAAM;IAAU,aAAa;IAAqC;GAC1E,SAAS;IAAE,MAAM;IAAU,aAAa;IAAoD;GAC7F;EACD,UAAU,CAAC,MAAM;EAClB;CACD,cAAc;EACZ,MAAM;EACN,YAAY;GACV,QAAQ;IAAE,MAAM;IAAU,aAAa;IAAoB;GAC3D,SAAS;IAAE,MAAM;IAAU,aAAa;IAAoB;GAC5D,MAAM;IACJ,MAAM;IACN,aACE;IACH;GACF;EACF;CACF;AAED,eAAsB,QAAQ,MAAoD;CAChF,MAAM,EAAE,KAAK,QAAQ,SAAS,MAAM,aAAa,YAAY;AAE7D,KAAI,CAAC,OAAO,OAAO,QAAQ,SACzB,QAAO;EAAE,QAAQ;EAAM,OAAO;EAAwD;AAGxF,KAAI;EACF,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,YAAY,WAAW;EAC7B,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,UAAU;EAE7D,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,QAAQ,UAAU;GACT;GACT,MAAM;GACN,QAAQ,WAAW;GACpB,CAAC;AAEF,eAAa,MAAM;EAEnB,MAAM,eAAe,MAAM,SAAS,MAAM;EAC1C,MAAM,kBAA0C,EAAE;AAClD,WAAS,QAAQ,SAAS,OAAO,QAAQ;AACvC,mBAAgB,OAAO;IACvB;EAGF,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe,IAAI;EAC5D,IAAI,eAAwB;AAC5B,MAAI,YAAY,SAAS,mBAAmB,CAC1C,KAAI;AACF,kBAAe,KAAK,MAAM,aAAa;UACjC;AAKV,SAAO,EACL,QAAQ;GACN,QAAQ,SAAS;GACjB,SAAS;GACT,MAAM;GACP,EACF;UACM,KAAK;AAEZ,SAAO;GAAE,QAAQ;GAAM,OAAO,wBADd,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACC"}