digital-tools 2.0.2 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/package.json +3 -4
  3. package/src/define.js +267 -0
  4. package/src/entities/advertising.js +999 -0
  5. package/src/entities/ai.js +756 -0
  6. package/src/entities/analytics.js +1588 -0
  7. package/src/entities/automation.js +601 -0
  8. package/src/entities/communication.js +1150 -0
  9. package/src/entities/crm.js +1386 -0
  10. package/src/entities/design.js +546 -0
  11. package/src/entities/development.js +2212 -0
  12. package/src/entities/document.js +874 -0
  13. package/src/entities/ecommerce.js +1429 -0
  14. package/src/entities/experiment.js +1039 -0
  15. package/src/entities/finance.js +3478 -0
  16. package/src/entities/forms.js +1892 -0
  17. package/src/entities/hr.js +661 -0
  18. package/src/entities/identity.js +997 -0
  19. package/src/entities/index.js +282 -0
  20. package/src/entities/infrastructure.js +1153 -0
  21. package/src/entities/knowledge.js +1438 -0
  22. package/src/entities/marketing.js +1610 -0
  23. package/src/entities/media.js +1634 -0
  24. package/src/entities/notification.js +1199 -0
  25. package/src/entities/presentation.js +1274 -0
  26. package/src/entities/productivity.js +1317 -0
  27. package/src/entities/project-management.js +1136 -0
  28. package/src/entities/recruiting.js +736 -0
  29. package/src/entities/shipping.js +509 -0
  30. package/src/entities/signature.js +1102 -0
  31. package/src/entities/site.js +222 -0
  32. package/src/entities/spreadsheet.js +1341 -0
  33. package/src/entities/storage.js +1198 -0
  34. package/src/entities/support.js +1166 -0
  35. package/src/entities/video-conferencing.js +1750 -0
  36. package/src/entities/video.js +950 -0
  37. package/src/entities.js +1663 -0
  38. package/src/index.js +74 -0
  39. package/src/providers/analytics/index.js +17 -0
  40. package/src/providers/analytics/mixpanel.js +255 -0
  41. package/src/providers/calendar/cal-com.js +303 -0
  42. package/src/providers/calendar/google-calendar.js +335 -0
  43. package/src/providers/calendar/index.js +20 -0
  44. package/src/providers/crm/hubspot.js +566 -0
  45. package/src/providers/crm/index.js +17 -0
  46. package/src/providers/development/github.js +472 -0
  47. package/src/providers/development/index.js +17 -0
  48. package/src/providers/ecommerce/index.js +17 -0
  49. package/src/providers/ecommerce/shopify.js +378 -0
  50. package/src/providers/email/index.js +20 -0
  51. package/src/providers/email/resend.js +258 -0
  52. package/src/providers/email/sendgrid.js +161 -0
  53. package/src/providers/finance/index.js +17 -0
  54. package/src/providers/finance/stripe.js +549 -0
  55. package/src/providers/forms/index.js +17 -0
  56. package/src/providers/forms/typeform.js +500 -0
  57. package/src/providers/index.js +123 -0
  58. package/src/providers/knowledge/index.js +17 -0
  59. package/src/providers/knowledge/notion.js +389 -0
  60. package/src/providers/marketing/index.js +17 -0
  61. package/src/providers/marketing/mailchimp.js +443 -0
  62. package/src/providers/media/cloudinary.js +318 -0
  63. package/src/providers/media/index.js +17 -0
  64. package/src/providers/messaging/index.js +20 -0
  65. package/src/providers/messaging/slack.js +393 -0
  66. package/src/providers/messaging/twilio-sms.js +249 -0
  67. package/src/providers/project-management/index.js +17 -0
  68. package/src/providers/project-management/linear.js +575 -0
  69. package/src/providers/registry.js +86 -0
  70. package/src/providers/spreadsheet/google-sheets.js +375 -0
  71. package/src/providers/spreadsheet/index.js +20 -0
  72. package/src/providers/spreadsheet/xlsx.js +423 -0
  73. package/src/providers/storage/index.js +24 -0
  74. package/src/providers/storage/s3.js +419 -0
  75. package/src/providers/support/index.js +17 -0
  76. package/src/providers/support/zendesk.js +373 -0
  77. package/src/providers/tasks/index.js +17 -0
  78. package/src/providers/tasks/todoist.js +286 -0
  79. package/src/providers/types.js +9 -0
  80. package/src/providers/video-conferencing/google-meet.js +286 -0
  81. package/src/providers/video-conferencing/index.js +31 -0
  82. package/src/providers/video-conferencing/jitsi.js +254 -0
  83. package/src/providers/video-conferencing/teams.js +270 -0
  84. package/src/providers/video-conferencing/zoom.js +332 -0
  85. package/src/registry.js +128 -0
  86. package/src/tools/communication.js +184 -0
  87. package/src/tools/data.js +205 -0
  88. package/src/tools/index.js +11 -0
  89. package/src/tools/web.js +137 -0
  90. package/src/types.js +10 -0
  91. package/test/define.test.js +306 -0
  92. package/test/registry.test.js +357 -0
  93. package/test/tools.test.js +363 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Tool Registry - Registration and discovery for tools
3
+ *
4
+ * Provides a central registry for tools that can be used by
5
+ * both humans and AI agents.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * In-memory tool registry implementation
11
+ */
12
+ class InMemoryToolRegistry {
13
+ tools = new Map();
14
+ register(tool) {
15
+ if (this.tools.has(tool.id)) {
16
+ console.warn(`Tool "${tool.id}" already registered, overwriting`);
17
+ }
18
+ this.tools.set(tool.id, tool);
19
+ }
20
+ unregister(id) {
21
+ return this.tools.delete(id);
22
+ }
23
+ get(id) {
24
+ return this.tools.get(id);
25
+ }
26
+ has(id) {
27
+ return this.tools.has(id);
28
+ }
29
+ list() {
30
+ return Array.from(this.tools.keys());
31
+ }
32
+ query(options) {
33
+ let results = Array.from(this.tools.values());
34
+ // Filter by category
35
+ if (options.category) {
36
+ results = results.filter((t) => t.category === options.category);
37
+ }
38
+ // Filter by subcategory
39
+ if (options.subcategory) {
40
+ results = results.filter((t) => t.subcategory === options.subcategory);
41
+ }
42
+ // Filter by tags
43
+ if (options.tags && options.tags.length > 0) {
44
+ results = results.filter((t) => t.tags && options.tags.some((tag) => t.tags.includes(tag)));
45
+ }
46
+ // Filter by audience
47
+ if (options.audience) {
48
+ results = results.filter((t) => t.audience === options.audience ||
49
+ t.audience === 'both' ||
50
+ !t.audience);
51
+ }
52
+ // Text search
53
+ if (options.search) {
54
+ const search = options.search.toLowerCase();
55
+ results = results.filter((t) => t.name.toLowerCase().includes(search) ||
56
+ t.description.toLowerCase().includes(search) ||
57
+ t.id.toLowerCase().includes(search));
58
+ }
59
+ // Pagination
60
+ const offset = options.offset ?? 0;
61
+ const limit = options.limit ?? results.length;
62
+ results = results.slice(offset, offset + limit);
63
+ return results;
64
+ }
65
+ byCategory(category) {
66
+ return this.query({ category });
67
+ }
68
+ clear() {
69
+ this.tools.clear();
70
+ }
71
+ }
72
+ /**
73
+ * Global tool registry instance
74
+ */
75
+ export const registry = new InMemoryToolRegistry();
76
+ /**
77
+ * Create a new registry instance (for isolated testing or namespacing)
78
+ */
79
+ export function createRegistry() {
80
+ return new InMemoryToolRegistry();
81
+ }
82
+ /**
83
+ * Convert a Tool to MCP format
84
+ */
85
+ export function toMCP(tool) {
86
+ return {
87
+ name: tool.id,
88
+ description: tool.description,
89
+ inputSchema: {
90
+ type: 'object',
91
+ properties: Object.fromEntries(tool.parameters.map((p) => [
92
+ p.name,
93
+ typeof p.schema === 'object' && 'type' in p.schema
94
+ ? { ...p.schema, description: p.description }
95
+ : { description: p.description },
96
+ ])),
97
+ required: tool.parameters.filter((p) => p.required).map((p) => p.name),
98
+ },
99
+ };
100
+ }
101
+ /**
102
+ * Convert all registered tools to MCP format
103
+ */
104
+ export function listMCPTools(reg = registry) {
105
+ return reg.list().map((id) => toMCP(reg.get(id)));
106
+ }
107
+ /**
108
+ * Register a tool in the global registry
109
+ */
110
+ export function registerTool(tool) {
111
+ registry.register(tool);
112
+ }
113
+ /**
114
+ * Get a tool from the global registry
115
+ */
116
+ export function getTool(id) {
117
+ return registry.get(id);
118
+ }
119
+ /**
120
+ * Execute a tool by ID
121
+ */
122
+ export async function executeTool(id, input) {
123
+ const tool = registry.get(id);
124
+ if (!tool) {
125
+ throw new Error(`Tool "${id}" not found`);
126
+ }
127
+ return tool.handler(input);
128
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Communication Tools - Email, messaging, notifications
3
+ *
4
+ * These tools define the interface for communication.
5
+ * Actual implementations would integrate with email services,
6
+ * Slack, etc. in production.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ import { defineTool } from '../define.js';
11
+ /**
12
+ * Send an email
13
+ */
14
+ export const sendEmail = defineTool({
15
+ id: 'communication.email.send',
16
+ name: 'Send Email',
17
+ description: 'Send an email to one or more recipients',
18
+ category: 'communication',
19
+ subcategory: 'email',
20
+ input: {
21
+ type: 'object',
22
+ properties: {
23
+ to: { type: 'array', items: { type: 'string' }, description: 'Recipient email addresses' },
24
+ cc: { type: 'array', items: { type: 'string' }, description: 'CC recipients' },
25
+ bcc: { type: 'array', items: { type: 'string' }, description: 'BCC recipients' },
26
+ subject: { type: 'string', description: 'Email subject' },
27
+ body: { type: 'string', description: 'Plain text body' },
28
+ html: { type: 'string', description: 'HTML body (optional)' },
29
+ attachments: {
30
+ type: 'array',
31
+ items: {
32
+ type: 'object',
33
+ properties: {
34
+ filename: { type: 'string' },
35
+ content: { type: 'string' },
36
+ contentType: { type: 'string' },
37
+ },
38
+ },
39
+ description: 'File attachments',
40
+ },
41
+ },
42
+ required: ['to', 'subject', 'body'],
43
+ },
44
+ handler: async (input) => {
45
+ // In production, this would integrate with an email service
46
+ // (SendGrid, SES, Postmark, etc.)
47
+ console.log('[Email] Sending to:', input.to.join(', '));
48
+ console.log('[Email] Subject:', input.subject);
49
+ // Simulate sending
50
+ return {
51
+ success: true,
52
+ messageId: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
53
+ };
54
+ },
55
+ options: {
56
+ audience: 'both',
57
+ tags: ['email', 'send', 'notify'],
58
+ requiresConfirmation: true,
59
+ permissions: [{ type: 'execute', resource: 'email' }],
60
+ },
61
+ });
62
+ /**
63
+ * Send a Slack message
64
+ */
65
+ export const sendSlackMessage = defineTool({
66
+ id: 'communication.slack.send',
67
+ name: 'Send Slack Message',
68
+ description: 'Send a message to a Slack channel or thread',
69
+ category: 'communication',
70
+ subcategory: 'slack',
71
+ input: {
72
+ type: 'object',
73
+ properties: {
74
+ channel: { type: 'string', description: 'Channel ID or name (e.g., #general)' },
75
+ text: { type: 'string', description: 'Message text' },
76
+ blocks: { type: 'array', description: 'Slack Block Kit blocks' },
77
+ threadTs: { type: 'string', description: 'Thread timestamp for replies' },
78
+ unfurlLinks: { type: 'boolean', description: 'Unfurl URLs in the message' },
79
+ },
80
+ required: ['channel', 'text'],
81
+ },
82
+ handler: async (input) => {
83
+ // In production, this would integrate with Slack API
84
+ console.log('[Slack] Sending to:', input.channel);
85
+ console.log('[Slack] Message:', input.text);
86
+ return {
87
+ success: true,
88
+ ts: `${Date.now()}.000001`,
89
+ channel: input.channel,
90
+ };
91
+ },
92
+ options: {
93
+ audience: 'both',
94
+ tags: ['slack', 'message', 'chat'],
95
+ permissions: [{ type: 'execute', resource: 'slack' }],
96
+ },
97
+ });
98
+ /**
99
+ * Send a notification
100
+ */
101
+ export const sendNotification = defineTool({
102
+ id: 'communication.notify',
103
+ name: 'Send Notification',
104
+ description: 'Send a notification through various channels',
105
+ category: 'communication',
106
+ subcategory: 'notification',
107
+ input: {
108
+ type: 'object',
109
+ properties: {
110
+ channel: {
111
+ type: 'string',
112
+ enum: ['email', 'slack', 'sms', 'push', 'webhook'],
113
+ description: 'Notification channel',
114
+ },
115
+ recipients: { type: 'array', items: { type: 'string' }, description: 'Recipients' },
116
+ title: { type: 'string', description: 'Notification title' },
117
+ message: { type: 'string', description: 'Notification message' },
118
+ priority: {
119
+ type: 'string',
120
+ enum: ['low', 'normal', 'high', 'urgent'],
121
+ description: 'Priority level',
122
+ },
123
+ data: { type: 'object', description: 'Additional data' },
124
+ },
125
+ required: ['channel', 'recipients', 'title', 'message'],
126
+ },
127
+ handler: async (input) => {
128
+ console.log(`[Notification] [${input.channel}] ${input.title}`);
129
+ console.log(`[Notification] Recipients:`, input.recipients);
130
+ console.log(`[Notification] Priority:`, input.priority || 'normal');
131
+ return {
132
+ success: true,
133
+ notificationId: `notif_${Date.now()}`,
134
+ delivered: input.recipients,
135
+ };
136
+ },
137
+ options: {
138
+ audience: 'both',
139
+ tags: ['notify', 'alert', 'message'],
140
+ },
141
+ });
142
+ /**
143
+ * Send an SMS
144
+ */
145
+ export const sendSms = defineTool({
146
+ id: 'communication.sms.send',
147
+ name: 'Send SMS',
148
+ description: 'Send an SMS text message',
149
+ category: 'communication',
150
+ subcategory: 'sms',
151
+ input: {
152
+ type: 'object',
153
+ properties: {
154
+ to: { type: 'string', description: 'Phone number (E.164 format)' },
155
+ message: { type: 'string', description: 'SMS message (max 160 chars recommended)' },
156
+ from: { type: 'string', description: 'Sender phone number or ID' },
157
+ },
158
+ required: ['to', 'message'],
159
+ },
160
+ handler: async (input) => {
161
+ // In production, this would integrate with Twilio, etc.
162
+ console.log('[SMS] Sending to:', input.to);
163
+ console.log('[SMS] Message:', input.message.slice(0, 160));
164
+ return {
165
+ success: true,
166
+ messageId: `sms_${Date.now()}`,
167
+ };
168
+ },
169
+ options: {
170
+ audience: 'both',
171
+ tags: ['sms', 'text', 'mobile'],
172
+ requiresConfirmation: true,
173
+ permissions: [{ type: 'execute', resource: 'sms' }],
174
+ },
175
+ });
176
+ /**
177
+ * All communication tools
178
+ */
179
+ export const communicationTools = [
180
+ sendEmail,
181
+ sendSlackMessage,
182
+ sendNotification,
183
+ sendSms,
184
+ ];
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Data Tools - Data manipulation, transformation, validation
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { defineTool } from '../define.js';
7
+ /**
8
+ * Parse JSON string
9
+ */
10
+ export const parseJson = defineTool({
11
+ id: 'data.json.parse',
12
+ name: 'Parse JSON',
13
+ description: 'Parse a JSON string into an object',
14
+ category: 'data',
15
+ subcategory: 'transform',
16
+ input: {
17
+ type: 'object',
18
+ properties: {
19
+ text: { type: 'string', description: 'JSON string to parse' },
20
+ },
21
+ required: ['text'],
22
+ },
23
+ handler: async (input) => {
24
+ try {
25
+ const data = JSON.parse(input.text);
26
+ return { data, valid: true };
27
+ }
28
+ catch (e) {
29
+ return {
30
+ data: null,
31
+ valid: false,
32
+ error: e instanceof Error ? e.message : 'Invalid JSON',
33
+ };
34
+ }
35
+ },
36
+ options: {
37
+ audience: 'both',
38
+ tags: ['json', 'parse', 'transform'],
39
+ idempotent: true,
40
+ },
41
+ });
42
+ /**
43
+ * Stringify object to JSON
44
+ */
45
+ export const stringifyJson = defineTool({
46
+ id: 'data.json.stringify',
47
+ name: 'Stringify JSON',
48
+ description: 'Convert an object to a JSON string',
49
+ category: 'data',
50
+ subcategory: 'transform',
51
+ input: {
52
+ type: 'object',
53
+ properties: {
54
+ data: { description: 'Data to stringify' },
55
+ pretty: { type: 'boolean', description: 'Pretty print with indentation' },
56
+ },
57
+ required: ['data'],
58
+ },
59
+ handler: async (input) => {
60
+ const text = input.pretty
61
+ ? JSON.stringify(input.data, null, 2)
62
+ : JSON.stringify(input.data);
63
+ return { text };
64
+ },
65
+ options: {
66
+ audience: 'both',
67
+ tags: ['json', 'stringify', 'transform'],
68
+ idempotent: true,
69
+ },
70
+ });
71
+ /**
72
+ * Parse CSV to array of objects
73
+ */
74
+ export const parseCsv = defineTool({
75
+ id: 'data.csv.parse',
76
+ name: 'Parse CSV',
77
+ description: 'Parse CSV text into an array of objects',
78
+ category: 'data',
79
+ subcategory: 'transform',
80
+ input: {
81
+ type: 'object',
82
+ properties: {
83
+ text: { type: 'string', description: 'CSV text to parse' },
84
+ delimiter: { type: 'string', description: 'Column delimiter (default: comma)' },
85
+ hasHeaders: { type: 'boolean', description: 'First row is headers (default: true)' },
86
+ },
87
+ required: ['text'],
88
+ },
89
+ handler: async (input) => {
90
+ const delimiter = input.delimiter || ',';
91
+ const hasHeaders = input.hasHeaders !== false;
92
+ const lines = input.text.split('\n').filter((line) => line.trim());
93
+ if (lines.length === 0) {
94
+ return { rows: [], headers: [], rowCount: 0 };
95
+ }
96
+ const headers = hasHeaders
97
+ ? lines[0].split(delimiter).map((h) => h.trim())
98
+ : lines[0].split(delimiter).map((_, i) => `column${i + 1}`);
99
+ const dataLines = hasHeaders ? lines.slice(1) : lines;
100
+ const rows = dataLines.map((line) => {
101
+ const values = line.split(delimiter).map((v) => v.trim());
102
+ const row = {};
103
+ headers.forEach((header, i) => {
104
+ row[header] = values[i] || '';
105
+ });
106
+ return row;
107
+ });
108
+ return { rows, headers, rowCount: rows.length };
109
+ },
110
+ options: {
111
+ audience: 'both',
112
+ tags: ['csv', 'parse', 'transform'],
113
+ idempotent: true,
114
+ },
115
+ });
116
+ /**
117
+ * Transform data using JSONPath-like expressions
118
+ */
119
+ export const transformData = defineTool({
120
+ id: 'data.transform',
121
+ name: 'Transform Data',
122
+ description: 'Transform data by mapping fields to new structure',
123
+ category: 'data',
124
+ subcategory: 'transform',
125
+ input: {
126
+ type: 'object',
127
+ properties: {
128
+ data: { description: 'Source data to transform' },
129
+ transform: {
130
+ type: 'object',
131
+ description: 'Mapping of output fields to input paths (e.g., { "name": "user.firstName" })',
132
+ },
133
+ },
134
+ required: ['data', 'transform'],
135
+ },
136
+ handler: async (input) => {
137
+ const result = {};
138
+ for (const [outputKey, inputPath] of Object.entries(input.transform)) {
139
+ const pathParts = inputPath.split('.');
140
+ let value = input.data;
141
+ for (const part of pathParts) {
142
+ if (value && typeof value === 'object' && part in value) {
143
+ value = value[part];
144
+ }
145
+ else {
146
+ value = undefined;
147
+ break;
148
+ }
149
+ }
150
+ result[outputKey] = value;
151
+ }
152
+ return { result };
153
+ },
154
+ options: {
155
+ audience: 'both',
156
+ tags: ['transform', 'map', 'extract'],
157
+ idempotent: true,
158
+ },
159
+ });
160
+ /**
161
+ * Filter array of objects
162
+ */
163
+ export const filterData = defineTool({
164
+ id: 'data.filter',
165
+ name: 'Filter Data',
166
+ description: 'Filter an array of objects by field values',
167
+ category: 'data',
168
+ subcategory: 'transform',
169
+ input: {
170
+ type: 'object',
171
+ properties: {
172
+ data: { type: 'array', description: 'Array of objects to filter' },
173
+ filter: { type: 'object', description: 'Filter criteria (e.g., { "status": "active" })' },
174
+ },
175
+ required: ['data', 'filter'],
176
+ },
177
+ handler: async (input) => {
178
+ const results = input.data.filter((item) => {
179
+ if (typeof item !== 'object' || item === null)
180
+ return false;
181
+ for (const [key, value] of Object.entries(input.filter)) {
182
+ if (item[key] !== value) {
183
+ return false;
184
+ }
185
+ }
186
+ return true;
187
+ });
188
+ return { results, count: results.length };
189
+ },
190
+ options: {
191
+ audience: 'both',
192
+ tags: ['filter', 'query', 'search'],
193
+ idempotent: true,
194
+ },
195
+ });
196
+ /**
197
+ * All data tools
198
+ */
199
+ export const dataTools = [
200
+ parseJson,
201
+ stringifyJson,
202
+ parseCsv,
203
+ transformData,
204
+ filterData,
205
+ ];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Common tool implementations
3
+ *
4
+ * Pre-built tools for common operations that can be used
5
+ * out of the box by both humans and AI agents.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export * from './web.js';
10
+ export * from './data.js';
11
+ export * from './communication.js';
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Web Tools - HTTP, scraping, browser automation
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { defineTool } from '../define.js';
7
+ /**
8
+ * Fetch content from a URL
9
+ */
10
+ export const fetchUrl = defineTool({
11
+ id: 'web.fetch',
12
+ name: 'Fetch URL',
13
+ description: 'Fetch content from a URL using HTTP',
14
+ category: 'web',
15
+ subcategory: 'fetch',
16
+ input: {
17
+ type: 'object',
18
+ properties: {
19
+ url: { type: 'string', description: 'URL to fetch' },
20
+ method: { type: 'string', description: 'HTTP method (default: GET)', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] },
21
+ headers: { type: 'object', description: 'Request headers' },
22
+ body: { type: 'string', description: 'Request body (for POST/PUT/PATCH)' },
23
+ },
24
+ required: ['url'],
25
+ },
26
+ handler: async (input) => {
27
+ const response = await fetch(input.url, {
28
+ method: input.method || 'GET',
29
+ headers: input.headers,
30
+ body: input.body,
31
+ });
32
+ const headers = {};
33
+ response.headers.forEach((value, key) => {
34
+ headers[key] = value;
35
+ });
36
+ return {
37
+ status: response.status,
38
+ headers,
39
+ body: await response.text(),
40
+ };
41
+ },
42
+ options: {
43
+ audience: 'both',
44
+ tags: ['http', 'network', 'api'],
45
+ },
46
+ });
47
+ /**
48
+ * Parse HTML and extract content
49
+ */
50
+ export const parseHtml = defineTool({
51
+ id: 'web.parse-html',
52
+ name: 'Parse HTML',
53
+ description: 'Parse HTML content and extract text, links, and images',
54
+ category: 'web',
55
+ subcategory: 'scrape',
56
+ input: {
57
+ type: 'object',
58
+ properties: {
59
+ html: { type: 'string', description: 'HTML content to parse' },
60
+ selector: { type: 'string', description: 'CSS selector to filter content (optional)' },
61
+ },
62
+ required: ['html'],
63
+ },
64
+ handler: async (input) => {
65
+ // Basic HTML parsing without DOM (would use cheerio or jsdom in production)
66
+ const text = input.html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
67
+ // Extract links
68
+ const linkRegex = /href="([^"]+)"/g;
69
+ const links = [];
70
+ let match;
71
+ while ((match = linkRegex.exec(input.html)) !== null) {
72
+ links.push(match[1]);
73
+ }
74
+ // Extract images
75
+ const imgRegex = /src="([^"]+)"/g;
76
+ const images = [];
77
+ while ((match = imgRegex.exec(input.html)) !== null) {
78
+ if (match[1].match(/\.(jpg|jpeg|png|gif|webp|svg)/i)) {
79
+ images.push(match[1]);
80
+ }
81
+ }
82
+ return { text, links, images };
83
+ },
84
+ options: {
85
+ audience: 'both',
86
+ tags: ['html', 'parse', 'extract'],
87
+ },
88
+ });
89
+ /**
90
+ * Read content from a URL (combines fetch + parse)
91
+ */
92
+ export const readUrl = defineTool({
93
+ id: 'web.read',
94
+ name: 'Read URL',
95
+ description: 'Read and extract content from a webpage',
96
+ category: 'web',
97
+ subcategory: 'scrape',
98
+ input: {
99
+ type: 'object',
100
+ properties: {
101
+ url: { type: 'string', description: 'URL to read' },
102
+ },
103
+ required: ['url'],
104
+ },
105
+ handler: async (input) => {
106
+ const response = await fetch(input.url);
107
+ const html = await response.text();
108
+ // Extract title
109
+ const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
110
+ const title = titleMatch ? titleMatch[1].trim() : '';
111
+ // Extract body text
112
+ const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
113
+ const body = bodyMatch ? bodyMatch[1] : html;
114
+ const text = body.replace(/<script[\s\S]*?<\/script>/gi, '')
115
+ .replace(/<style[\s\S]*?<\/style>/gi, '')
116
+ .replace(/<[^>]*>/g, ' ')
117
+ .replace(/\s+/g, ' ')
118
+ .trim();
119
+ // Extract links
120
+ const linkRegex = /href="(https?:\/\/[^"]+)"/g;
121
+ const links = [];
122
+ let match;
123
+ while ((match = linkRegex.exec(html)) !== null) {
124
+ links.push(match[1]);
125
+ }
126
+ return { title, text: text.slice(0, 10000), links: [...new Set(links)] };
127
+ },
128
+ options: {
129
+ audience: 'both',
130
+ tags: ['read', 'scrape', 'extract'],
131
+ idempotent: true,
132
+ },
133
+ });
134
+ /**
135
+ * All web tools
136
+ */
137
+ export const webTools = [fetchUrl, parseHtml, readUrl];
package/src/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Types for digital-tools
3
+ *
4
+ * Core types for tools that can be used by both humans and AI agents.
5
+ * This package provides the foundational tool primitives used across
6
+ * autonomous-agents, human-in-the-loop, and services-as-software.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ export {};