@agenticmail/enterprise 0.5.320 → 0.5.322
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/CHANGELOG.md +68 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/SECURITY.md +42 -0
- package/dist/agent-tools-BNSTIK4P.js +13949 -0
- package/dist/chunk-7HBGXW7Z.js +4929 -0
- package/dist/chunk-RF2LGX3E.js +1519 -0
- package/dist/chunk-YCWOCIPH.js +5101 -0
- package/dist/cli-agent-ION2W5JF.js +2441 -0
- package/dist/cli-serve-53FVANQK.js +260 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +3 -3
- package/dist/microsoft-2IIHDLJB.js +4159 -0
- package/dist/runtime-OVGSOTAH.js +45 -0
- package/dist/server-6NDO2S52.js +28 -0
- package/dist/setup-T6KYFR7O.js +20 -0
- package/package.json +31 -3
- package/src/agent-tools/tools/enterprise-database.ts +2 -2
- package/src/agent-tools/tools/microsoft/onedrive.ts +203 -2
- package/.github/CODEOWNERS +0 -23
- package/.github/workflows/publish-community-skills.yml +0 -121
- package/.github/workflows/validate-community-skills.yml +0 -172
- package/agriculture_southwest_nigeria_research.txt +0 -10
- package/boa_credit_cards_research.txt +0 -10
- package/customer_support_research_feb2026.txt +0 -10
- package/ecosystem.config.cjs +0 -120
- package/src/dashboard/docs/_template.txt +0 -92
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentRuntime,
|
|
3
|
+
EmailChannel,
|
|
4
|
+
FollowUpScheduler,
|
|
5
|
+
SessionManager,
|
|
6
|
+
SubAgentManager,
|
|
7
|
+
ToolRegistry,
|
|
8
|
+
callLLM,
|
|
9
|
+
createAgentRuntime,
|
|
10
|
+
createNoopHooks,
|
|
11
|
+
createRuntimeHooks,
|
|
12
|
+
estimateMessageTokens,
|
|
13
|
+
estimateTokens,
|
|
14
|
+
executeTool,
|
|
15
|
+
runAgentLoop,
|
|
16
|
+
toolsToDefinitions
|
|
17
|
+
} from "./chunk-7HBGXW7Z.js";
|
|
18
|
+
import {
|
|
19
|
+
PROVIDER_REGISTRY,
|
|
20
|
+
listAllProviders,
|
|
21
|
+
resolveApiKeyForProvider,
|
|
22
|
+
resolveProvider
|
|
23
|
+
} from "./chunk-UF3ZJMJO.js";
|
|
24
|
+
import "./chunk-KFQGP6VL.js";
|
|
25
|
+
export {
|
|
26
|
+
AgentRuntime,
|
|
27
|
+
EmailChannel,
|
|
28
|
+
FollowUpScheduler,
|
|
29
|
+
PROVIDER_REGISTRY,
|
|
30
|
+
SessionManager,
|
|
31
|
+
SubAgentManager,
|
|
32
|
+
ToolRegistry,
|
|
33
|
+
callLLM,
|
|
34
|
+
createAgentRuntime,
|
|
35
|
+
createNoopHooks,
|
|
36
|
+
createRuntimeHooks,
|
|
37
|
+
estimateMessageTokens,
|
|
38
|
+
estimateTokens,
|
|
39
|
+
executeTool,
|
|
40
|
+
listAllProviders,
|
|
41
|
+
resolveApiKeyForProvider,
|
|
42
|
+
resolveProvider,
|
|
43
|
+
runAgentLoop,
|
|
44
|
+
toolsToDefinitions
|
|
45
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-YCWOCIPH.js";
|
|
4
|
+
import "./chunk-DJBCRQTD.js";
|
|
5
|
+
import "./chunk-UF3ZJMJO.js";
|
|
6
|
+
import "./chunk-CAHNZGGK.js";
|
|
7
|
+
import "./chunk-Z7NVD3OQ.js";
|
|
8
|
+
import "./chunk-VSBC4SWO.js";
|
|
9
|
+
import "./chunk-AF3WSNVX.js";
|
|
10
|
+
import "./chunk-74ZCQKYU.js";
|
|
11
|
+
import "./chunk-Z6K5FKAB.js";
|
|
12
|
+
import "./chunk-C6JP5NR6.js";
|
|
13
|
+
import "./chunk-BQM7MBPS.js";
|
|
14
|
+
import "./chunk-3UAFHUEC.js";
|
|
15
|
+
import "./chunk-6C5PKREN.js";
|
|
16
|
+
import "./chunk-3FMK32KQ.js";
|
|
17
|
+
import "./chunk-YDD5TC5Q.js";
|
|
18
|
+
import "./chunk-37ABTUFU.js";
|
|
19
|
+
import "./chunk-NU657BBQ.js";
|
|
20
|
+
import "./chunk-PGAU3W3M.js";
|
|
21
|
+
import "./chunk-FLQ5FLHW.js";
|
|
22
|
+
import "./chunk-WUAWWKTN.js";
|
|
23
|
+
import "./chunk-ZGYVXYQQ.js";
|
|
24
|
+
import "./chunk-22U7TZPN.js";
|
|
25
|
+
import "./chunk-KFQGP6VL.js";
|
|
26
|
+
export {
|
|
27
|
+
createServer
|
|
28
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-RF2LGX3E.js";
|
|
10
|
+
import "./chunk-4EKXYIJF.js";
|
|
11
|
+
import "./chunk-KFQGP6VL.js";
|
|
12
|
+
export {
|
|
13
|
+
promptCompanyInfo,
|
|
14
|
+
promptDatabase,
|
|
15
|
+
promptDeployment,
|
|
16
|
+
promptDomain,
|
|
17
|
+
promptRegistration,
|
|
18
|
+
provision,
|
|
19
|
+
runSetupWizard
|
|
20
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agenticmail/enterprise",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.322",
|
|
4
4
|
"description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,10 +28,31 @@
|
|
|
28
28
|
"saml",
|
|
29
29
|
"oidc",
|
|
30
30
|
"scim",
|
|
31
|
-
"identity"
|
|
31
|
+
"identity",
|
|
32
|
+
"ai-agents",
|
|
33
|
+
"agent-platform",
|
|
34
|
+
"microsoft-365",
|
|
35
|
+
"google-workspace",
|
|
36
|
+
"compliance",
|
|
37
|
+
"soc2",
|
|
38
|
+
"gdpr",
|
|
39
|
+
"dlp",
|
|
40
|
+
"data-loss-prevention",
|
|
41
|
+
"oauth",
|
|
42
|
+
"multi-tenant",
|
|
43
|
+
"workforce-management",
|
|
44
|
+
"task-pipeline",
|
|
45
|
+
"telegram-bot",
|
|
46
|
+
"whatsapp-bot",
|
|
47
|
+
"voice-ai",
|
|
48
|
+
"google-meet",
|
|
49
|
+
"dependency-management",
|
|
50
|
+
"audit-log",
|
|
51
|
+
"rbac",
|
|
52
|
+
"encryption"
|
|
32
53
|
],
|
|
33
54
|
"author": "Ope Olatunji (https://github.com/ope-olatunji)",
|
|
34
|
-
"homepage": "https://
|
|
55
|
+
"homepage": "https://agenticmail.io",
|
|
35
56
|
"repository": {
|
|
36
57
|
"type": "git",
|
|
37
58
|
"url": "https://github.com/agenticmail/enterprise.git"
|
|
@@ -90,5 +111,12 @@
|
|
|
90
111
|
"mongodb": "^6.3.0",
|
|
91
112
|
"mysql2": "^3.9.0",
|
|
92
113
|
"postgres": "^3.4.0"
|
|
114
|
+
},
|
|
115
|
+
"engines": {
|
|
116
|
+
"node": ">=18.0.0"
|
|
117
|
+
},
|
|
118
|
+
"funding": {
|
|
119
|
+
"type": "individual",
|
|
120
|
+
"url": "https://agenticmail.io"
|
|
93
121
|
}
|
|
94
122
|
}
|
|
@@ -107,7 +107,7 @@ export function createDatabaseTools(options?: ToolCreationOptions): AnyAgentTool
|
|
|
107
107
|
var entDbSchema: AnyAgentTool = {
|
|
108
108
|
name: 'ent_db_schema',
|
|
109
109
|
label: 'Database Schema',
|
|
110
|
-
description: 'Get the schema
|
|
110
|
+
description: 'Get the schema of a table in a LOCAL SQLite database file. For external/cloud databases granted via dashboard, use db_describe_table instead.',
|
|
111
111
|
category: 'utility',
|
|
112
112
|
risk: 'low',
|
|
113
113
|
parameters: {
|
|
@@ -169,7 +169,7 @@ export function createDatabaseTools(options?: ToolCreationOptions): AnyAgentTool
|
|
|
169
169
|
var entDbExplain: AnyAgentTool = {
|
|
170
170
|
name: 'ent_db_explain',
|
|
171
171
|
label: 'Explain Query Plan',
|
|
172
|
-
description: 'Run EXPLAIN QUERY PLAN on a
|
|
172
|
+
description: 'Run EXPLAIN QUERY PLAN on a LOCAL SQLite database file. For external/cloud databases granted via dashboard, use db_explain instead.',
|
|
173
173
|
category: 'utility',
|
|
174
174
|
risk: 'low',
|
|
175
175
|
parameters: {
|
|
@@ -210,17 +210,218 @@ export function createOneDriveTools(config: MicrosoftToolsConfig, _options?: Too
|
|
|
210
210
|
itemId: { type: 'string', description: 'Item ID to share' },
|
|
211
211
|
type: { type: 'string', description: 'view or edit (default: view)' },
|
|
212
212
|
scope: { type: 'string', description: 'anonymous (anyone) or organization (default: organization)' },
|
|
213
|
+
expirationDateTime: { type: 'string', description: 'Link expiry (ISO 8601). Only for anonymous links.' },
|
|
214
|
+
password: { type: 'string', description: 'Password-protect the link (only for anonymous links).' },
|
|
213
215
|
},
|
|
214
216
|
required: ['itemId'],
|
|
215
217
|
},
|
|
216
218
|
async execute(_id: string, params: any) {
|
|
217
219
|
try {
|
|
218
220
|
const token = await tp.getAccessToken();
|
|
221
|
+
const body: any = { type: params.type || 'view', scope: params.scope || 'organization' };
|
|
222
|
+
if (params.expirationDateTime) body.expirationDateTime = params.expirationDateTime;
|
|
223
|
+
if (params.password) body.password = params.password;
|
|
219
224
|
const link = await graph(token, `/me/drive/items/${params.itemId}/createLink`, {
|
|
225
|
+
method: 'POST', body,
|
|
226
|
+
});
|
|
227
|
+
return jsonResult({ url: link.link?.webUrl, type: link.link?.type, scope: link.link?.scope, expiration: link.link?.expirationDateTime });
|
|
228
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
{
|
|
233
|
+
name: 'onedrive_move',
|
|
234
|
+
description: 'Move or rename a file/folder in OneDrive.',
|
|
235
|
+
category: 'utility' as const,
|
|
236
|
+
parameters: {
|
|
237
|
+
type: 'object' as const,
|
|
238
|
+
properties: {
|
|
239
|
+
itemId: { type: 'string', description: 'Item ID to move/rename' },
|
|
240
|
+
newName: { type: 'string', description: 'New name (optional, for rename)' },
|
|
241
|
+
destinationFolderId: { type: 'string', description: 'Destination folder ID (optional, for move)' },
|
|
242
|
+
destinationPath: { type: 'string', description: 'Destination folder path (alternative to ID, e.g., "/Documents/Archive")' },
|
|
243
|
+
},
|
|
244
|
+
required: ['itemId'],
|
|
245
|
+
},
|
|
246
|
+
async execute(_id: string, params: any) {
|
|
247
|
+
try {
|
|
248
|
+
const token = await tp.getAccessToken();
|
|
249
|
+
const body: any = {};
|
|
250
|
+
if (params.newName) body.name = params.newName;
|
|
251
|
+
if (params.destinationFolderId) {
|
|
252
|
+
body.parentReference = { id: params.destinationFolderId };
|
|
253
|
+
} else if (params.destinationPath) {
|
|
254
|
+
// Resolve destination folder ID first
|
|
255
|
+
const dest = await graph(token, `/me/drive/root:${params.destinationPath}`, {
|
|
256
|
+
query: { '$select': 'id' },
|
|
257
|
+
});
|
|
258
|
+
body.parentReference = { id: dest.id };
|
|
259
|
+
}
|
|
260
|
+
if (!body.name && !body.parentReference) {
|
|
261
|
+
return errorResult('Provide newName (rename), destinationFolderId or destinationPath (move), or both.');
|
|
262
|
+
}
|
|
263
|
+
const updated = await graph(token, `/me/drive/items/${params.itemId}`, {
|
|
264
|
+
method: 'PATCH', body,
|
|
265
|
+
});
|
|
266
|
+
return jsonResult({ id: updated.id, name: updated.name, webUrl: updated.webUrl, path: updated.parentReference?.path });
|
|
267
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
{
|
|
272
|
+
name: 'onedrive_copy',
|
|
273
|
+
description: 'Copy a file or folder to a new location in OneDrive. Returns a monitor URL for large copies.',
|
|
274
|
+
category: 'utility' as const,
|
|
275
|
+
parameters: {
|
|
276
|
+
type: 'object' as const,
|
|
277
|
+
properties: {
|
|
278
|
+
itemId: { type: 'string', description: 'Item ID to copy' },
|
|
279
|
+
destinationFolderId: { type: 'string', description: 'Destination folder ID' },
|
|
280
|
+
destinationPath: { type: 'string', description: 'Destination folder path (alternative to ID)' },
|
|
281
|
+
newName: { type: 'string', description: 'New name for the copy (optional)' },
|
|
282
|
+
},
|
|
283
|
+
required: ['itemId'],
|
|
284
|
+
},
|
|
285
|
+
async execute(_id: string, params: any) {
|
|
286
|
+
try {
|
|
287
|
+
const token = await tp.getAccessToken();
|
|
288
|
+
const body: any = {};
|
|
289
|
+
if (params.newName) body.name = params.newName;
|
|
290
|
+
if (params.destinationFolderId) {
|
|
291
|
+
body.parentReference = { driveId: 'me', id: params.destinationFolderId };
|
|
292
|
+
} else if (params.destinationPath) {
|
|
293
|
+
const dest = await graph(token, `/me/drive/root:${params.destinationPath}`, { query: { '$select': 'id,parentReference' } });
|
|
294
|
+
body.parentReference = { driveId: dest.parentReference?.driveId || 'me', id: dest.id };
|
|
295
|
+
} else {
|
|
296
|
+
return errorResult('Provide destinationFolderId or destinationPath.');
|
|
297
|
+
}
|
|
298
|
+
// Copy returns 202 with Location header (async operation)
|
|
299
|
+
const res = await fetch(`https://graph.microsoft.com/v1.0/me/drive/items/${params.itemId}/copy`, {
|
|
220
300
|
method: 'POST',
|
|
221
|
-
|
|
301
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
302
|
+
body: JSON.stringify(body),
|
|
222
303
|
});
|
|
223
|
-
|
|
304
|
+
if (res.status === 202) {
|
|
305
|
+
return jsonResult({ status: 'copying', monitorUrl: res.headers.get('Location'), message: 'Copy started. Large files may take time.' });
|
|
306
|
+
}
|
|
307
|
+
if (!res.ok) throw new Error(`Copy failed: ${res.status} ${await res.text()}`);
|
|
308
|
+
const data = await res.json();
|
|
309
|
+
return jsonResult({ id: data.id, name: data.name, webUrl: data.webUrl });
|
|
310
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
{
|
|
315
|
+
name: 'onedrive_versions',
|
|
316
|
+
description: 'List version history of a file in OneDrive.',
|
|
317
|
+
category: 'utility' as const,
|
|
318
|
+
parameters: {
|
|
319
|
+
type: 'object' as const,
|
|
320
|
+
properties: {
|
|
321
|
+
itemId: { type: 'string', description: 'File item ID' },
|
|
322
|
+
path: { type: 'string', description: 'File path (alternative to itemId)' },
|
|
323
|
+
},
|
|
324
|
+
required: [],
|
|
325
|
+
},
|
|
326
|
+
async execute(_id: string, params: any) {
|
|
327
|
+
try {
|
|
328
|
+
const token = await tp.getAccessToken();
|
|
329
|
+
const base = params.itemId ? `/me/drive/items/${params.itemId}` : `/me/drive/root:${params.path}:`;
|
|
330
|
+
const data = await graph(token, `${base}/versions`);
|
|
331
|
+
const versions = (data.value || []).map((v: any) => ({
|
|
332
|
+
id: v.id,
|
|
333
|
+
size: v.size,
|
|
334
|
+
modified: v.lastModifiedDateTime,
|
|
335
|
+
modifiedBy: v.lastModifiedBy?.user?.displayName || v.lastModifiedBy?.user?.email,
|
|
336
|
+
}));
|
|
337
|
+
return jsonResult({ versions, count: versions.length });
|
|
338
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
{
|
|
343
|
+
name: 'onedrive_recent',
|
|
344
|
+
description: 'List recently accessed files in OneDrive.',
|
|
345
|
+
category: 'utility' as const,
|
|
346
|
+
parameters: {
|
|
347
|
+
type: 'object' as const,
|
|
348
|
+
properties: {
|
|
349
|
+
maxResults: { type: 'number', description: 'Max items (default: 20)' },
|
|
350
|
+
},
|
|
351
|
+
required: [],
|
|
352
|
+
},
|
|
353
|
+
async execute(_id: string, params: any) {
|
|
354
|
+
try {
|
|
355
|
+
const token = await tp.getAccessToken();
|
|
356
|
+
const data = await graph(token, '/me/drive/recent', {
|
|
357
|
+
query: { '$top': String(params.maxResults || 20), '$select': 'id,name,size,webUrl,lastModifiedDateTime,file,remoteItem' },
|
|
358
|
+
});
|
|
359
|
+
const items = (data.value || []).map((i: any) => {
|
|
360
|
+
const item = i.remoteItem || i;
|
|
361
|
+
return {
|
|
362
|
+
id: item.id, name: item.name || i.name, size: item.size,
|
|
363
|
+
mimeType: item.file?.mimeType,
|
|
364
|
+
modified: item.lastModifiedDateTime || i.lastModifiedDateTime,
|
|
365
|
+
webUrl: item.webUrl || i.webUrl,
|
|
366
|
+
};
|
|
367
|
+
});
|
|
368
|
+
return jsonResult({ items, count: items.length });
|
|
369
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
|
|
373
|
+
{
|
|
374
|
+
name: 'onedrive_permissions',
|
|
375
|
+
description: 'List or manage sharing permissions on a file/folder.',
|
|
376
|
+
category: 'utility' as const,
|
|
377
|
+
parameters: {
|
|
378
|
+
type: 'object' as const,
|
|
379
|
+
properties: {
|
|
380
|
+
itemId: { type: 'string', description: 'Item ID' },
|
|
381
|
+
action: { type: 'string', description: 'list (default), revoke, or invite' },
|
|
382
|
+
permissionId: { type: 'string', description: 'Permission ID to revoke (for action=revoke)' },
|
|
383
|
+
email: { type: 'string', description: 'Email to invite (for action=invite)' },
|
|
384
|
+
role: { type: 'string', description: 'read or write (for action=invite, default: read)' },
|
|
385
|
+
message: { type: 'string', description: 'Optional message for invite' },
|
|
386
|
+
},
|
|
387
|
+
required: ['itemId'],
|
|
388
|
+
},
|
|
389
|
+
async execute(_id: string, params: any) {
|
|
390
|
+
try {
|
|
391
|
+
const token = await tp.getAccessToken();
|
|
392
|
+
const action = params.action || 'list';
|
|
393
|
+
|
|
394
|
+
if (action === 'revoke') {
|
|
395
|
+
if (!params.permissionId) return errorResult('permissionId required for revoke');
|
|
396
|
+
await graph(token, `/me/drive/items/${params.itemId}/permissions/${params.permissionId}`, { method: 'DELETE' });
|
|
397
|
+
return jsonResult({ revoked: true, permissionId: params.permissionId });
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (action === 'invite') {
|
|
401
|
+
if (!params.email) return errorResult('email required for invite');
|
|
402
|
+
const invite = await graph(token, `/me/drive/items/${params.itemId}/invite`, {
|
|
403
|
+
method: 'POST',
|
|
404
|
+
body: {
|
|
405
|
+
recipients: [{ email: params.email }],
|
|
406
|
+
roles: [params.role || 'read'],
|
|
407
|
+
requireSignIn: true,
|
|
408
|
+
sendInvitation: true,
|
|
409
|
+
message: params.message || '',
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
return jsonResult({ invited: true, email: params.email, permissions: invite.value });
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Default: list
|
|
416
|
+
const data = await graph(token, `/me/drive/items/${params.itemId}/permissions`);
|
|
417
|
+
const perms = (data.value || []).map((p: any) => ({
|
|
418
|
+
id: p.id,
|
|
419
|
+
roles: p.roles,
|
|
420
|
+
grantedTo: p.grantedToV2?.user?.displayName || p.grantedTo?.user?.displayName,
|
|
421
|
+
email: p.grantedToV2?.user?.email || p.invitation?.email,
|
|
422
|
+
link: p.link ? { type: p.link.type, scope: p.link.scope, webUrl: p.link.webUrl } : undefined,
|
|
423
|
+
}));
|
|
424
|
+
return jsonResult({ permissions: perms, count: perms.length });
|
|
224
425
|
} catch (e: any) { return errorResult(e.message); }
|
|
225
426
|
},
|
|
226
427
|
},
|
package/.github/CODEOWNERS
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# ─── AgenticMail Enterprise Code Owners ───────────────
|
|
2
|
-
#
|
|
3
|
-
# These owners are automatically requested for review
|
|
4
|
-
# when a PR touches matching paths.
|
|
5
|
-
#
|
|
6
|
-
# IMPORTANT: Enable "Require review from Code Owners" in
|
|
7
|
-
# GitHub repo Settings → Branches → Branch protection rules
|
|
8
|
-
# for the `main` branch to enforce this.
|
|
9
|
-
|
|
10
|
-
# Core engine — requires core team review
|
|
11
|
-
src/engine/ @agenticmail/core-team
|
|
12
|
-
src/server.ts @agenticmail/core-team
|
|
13
|
-
src/auth/ @agenticmail/core-team
|
|
14
|
-
src/admin/ @agenticmail/core-team
|
|
15
|
-
|
|
16
|
-
# Community skills — ALL submissions require maintainer review
|
|
17
|
-
# No community skill can be merged without explicit approval
|
|
18
|
-
community-skills/ @agenticmail/skill-reviewers
|
|
19
|
-
|
|
20
|
-
# CI/CD and repo config — requires admin review
|
|
21
|
-
.github/ @agenticmail/core-team
|
|
22
|
-
CLAUDE.md @agenticmail/core-team
|
|
23
|
-
package.json @agenticmail/core-team
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
name: Publish Community Skills
|
|
2
|
-
|
|
3
|
-
# IMPORTANT: This workflow only runs AFTER code is merged into main.
|
|
4
|
-
# It does NOT auto-approve or auto-merge PRs.
|
|
5
|
-
# All community skill PRs must be reviewed and approved by a maintainer
|
|
6
|
-
# (enforced by CODEOWNERS + branch protection rules) before this runs.
|
|
7
|
-
#
|
|
8
|
-
# After a maintainer-approved merge:
|
|
9
|
-
# 1. Rebuilds community-skills/index.json (the registry index deployed instances fetch)
|
|
10
|
-
# 2. Optionally pushes each skill to a live registry API
|
|
11
|
-
|
|
12
|
-
on:
|
|
13
|
-
push:
|
|
14
|
-
branches: [main]
|
|
15
|
-
paths:
|
|
16
|
-
- 'community-skills/**'
|
|
17
|
-
|
|
18
|
-
permissions:
|
|
19
|
-
contents: write
|
|
20
|
-
|
|
21
|
-
jobs:
|
|
22
|
-
publish:
|
|
23
|
-
runs-on: ubuntu-latest
|
|
24
|
-
steps:
|
|
25
|
-
- uses: actions/checkout@v4
|
|
26
|
-
with:
|
|
27
|
-
fetch-depth: 2
|
|
28
|
-
|
|
29
|
-
- uses: actions/setup-node@v4
|
|
30
|
-
with:
|
|
31
|
-
node-version: '20'
|
|
32
|
-
|
|
33
|
-
# ── Generate index.json ─────────────────────────
|
|
34
|
-
# This is the file that deployed instances fetch to discover new skills.
|
|
35
|
-
# Every community-skills/<id>/agenticmail-skill.json gets listed here.
|
|
36
|
-
|
|
37
|
-
- name: Generate community-skills/index.json
|
|
38
|
-
run: |
|
|
39
|
-
node -e "
|
|
40
|
-
const fs = require('fs');
|
|
41
|
-
const path = require('path');
|
|
42
|
-
const dir = 'community-skills';
|
|
43
|
-
const skills = [];
|
|
44
|
-
|
|
45
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
46
|
-
if (!entry.isDirectory() || entry.name.startsWith('_') || entry.name.startsWith('.')) continue;
|
|
47
|
-
const manifestPath = path.join(dir, entry.name, 'agenticmail-skill.json');
|
|
48
|
-
try {
|
|
49
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
50
|
-
skills.push({
|
|
51
|
-
id: manifest.id,
|
|
52
|
-
name: manifest.name,
|
|
53
|
-
version: manifest.version,
|
|
54
|
-
author: manifest.author,
|
|
55
|
-
category: manifest.category,
|
|
56
|
-
risk: manifest.risk,
|
|
57
|
-
description: manifest.description,
|
|
58
|
-
});
|
|
59
|
-
} catch (err) {
|
|
60
|
-
console.error('Skipping ' + entry.name + ': ' + err.message);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const index = {
|
|
65
|
-
generatedAt: new Date().toISOString(),
|
|
66
|
-
count: skills.length,
|
|
67
|
-
skills,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
fs.writeFileSync(path.join(dir, 'index.json'), JSON.stringify(index, null, 2) + '\n');
|
|
71
|
-
console.log('Generated index.json with ' + skills.length + ' skills');
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
- name: Commit index.json
|
|
75
|
-
run: |
|
|
76
|
-
git config user.name "github-actions[bot]"
|
|
77
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
78
|
-
git add community-skills/index.json
|
|
79
|
-
git diff --cached --quiet && echo "No changes to index.json" && exit 0
|
|
80
|
-
git commit -m "chore: update community-skills index [skip ci]"
|
|
81
|
-
git push
|
|
82
|
-
|
|
83
|
-
# ── Publish to live registry (optional) ─────────
|
|
84
|
-
# If ENTERPRISE_REGISTRY_URL is configured, also push directly to a live server.
|
|
85
|
-
|
|
86
|
-
- name: Find changed skill directories
|
|
87
|
-
id: changed
|
|
88
|
-
run: |
|
|
89
|
-
CHANGED=$(git diff --name-only HEAD~1 HEAD -- community-skills/ || true)
|
|
90
|
-
DIRS=$(echo "$CHANGED" | grep -oP 'community-skills/[^/]+' | sort -u | grep -v '_template' || true)
|
|
91
|
-
echo "dirs<<EOF" >> $GITHUB_OUTPUT
|
|
92
|
-
echo "$DIRS" >> $GITHUB_OUTPUT
|
|
93
|
-
echo "EOF" >> $GITHUB_OUTPUT
|
|
94
|
-
COUNT=$(echo "$DIRS" | grep -c '.' || echo "0")
|
|
95
|
-
echo "count=$COUNT" >> $GITHUB_OUTPUT
|
|
96
|
-
|
|
97
|
-
- name: Publish to live registry
|
|
98
|
-
if: steps.changed.outputs.count != '0' && env.REGISTRY_URL != ''
|
|
99
|
-
env:
|
|
100
|
-
REGISTRY_URL: ${{ secrets.ENTERPRISE_REGISTRY_URL }}
|
|
101
|
-
REGISTRY_TOKEN: ${{ secrets.ENTERPRISE_API_KEY }}
|
|
102
|
-
run: |
|
|
103
|
-
while IFS= read -r dir; do
|
|
104
|
-
[ -z "$dir" ] && continue
|
|
105
|
-
SKILL_ID=$(basename "$dir")
|
|
106
|
-
MANIFEST="$dir/agenticmail-skill.json"
|
|
107
|
-
[ ! -f "$MANIFEST" ] && continue
|
|
108
|
-
|
|
109
|
-
echo "Publishing $SKILL_ID..."
|
|
110
|
-
HTTP_CODE=$(curl -s -o /tmp/response.json -w "%{http_code}" \
|
|
111
|
-
-X POST "${REGISTRY_URL}/api/engine/community/skills/publish" \
|
|
112
|
-
-H "Content-Type: application/json" \
|
|
113
|
-
-H "Authorization: Bearer ${REGISTRY_TOKEN}" \
|
|
114
|
-
-d @"$MANIFEST")
|
|
115
|
-
|
|
116
|
-
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
|
|
117
|
-
echo "Published $SKILL_ID (HTTP $HTTP_CODE)"
|
|
118
|
-
else
|
|
119
|
-
echo "::warning::Failed to publish $SKILL_ID (HTTP $HTTP_CODE)"
|
|
120
|
-
fi
|
|
121
|
-
done <<< "${{ steps.changed.outputs.dirs }}"
|