@framers/agentos-ext-cloud-cloudflare-pages 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.
package/src/index.ts ADDED
@@ -0,0 +1,129 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @fileoverview Cloudflare Pages & Workers Extension for AgentOS.
4
+ *
5
+ * Provides 4 tools for deploying Pages projects, managing DNS records,
6
+ * and deploying Worker scripts via the Cloudflare API v4.
7
+ *
8
+ * @module @framers/agentos-ext-cloud-cloudflare-pages
9
+ */
10
+
11
+ import { CloudflareService } from './CloudflareService.js';
12
+ import type { CloudflareConfig } from './CloudflareService.js';
13
+ import { CfDeployPagesTool } from './tools/deploy.js';
14
+ import { CfListProjectsTool } from './tools/listProjects.js';
15
+ import { CfConfigureDnsTool } from './tools/configureDns.js';
16
+ import { CfCreateWorkerTool } from './tools/createWorker.js';
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Options
20
+ // ---------------------------------------------------------------------------
21
+
22
+ export interface CloudflarePagesOptions {
23
+ apiToken?: string;
24
+ accountId?: string;
25
+ secrets?: Record<string, string>;
26
+ }
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Secret Resolution
30
+ // ---------------------------------------------------------------------------
31
+
32
+ function resolveConfig(opts: CloudflarePagesOptions, secrets: Record<string, string>): CloudflareConfig {
33
+ return {
34
+ apiToken:
35
+ opts.apiToken ?? secrets['cloudflare.apiToken']
36
+ ?? process.env.CLOUDFLARE_API_TOKEN ?? '',
37
+ accountId:
38
+ opts.accountId ?? secrets['cloudflare.accountId']
39
+ ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? '',
40
+ };
41
+ }
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Extension Context
45
+ // ---------------------------------------------------------------------------
46
+
47
+ export interface ExtensionContext {
48
+ options?: Record<string, unknown>;
49
+ secrets?: Record<string, string>;
50
+ getSecret?: (key: string) => string | undefined;
51
+ logger?: { info: (msg: string) => void };
52
+ }
53
+
54
+ export interface ExtensionPack {
55
+ name: string;
56
+ version: string;
57
+ descriptors: Array<{ id: string; kind: string; priority?: number; payload: unknown }>;
58
+ onActivate?: () => Promise<void>;
59
+ onDeactivate?: () => Promise<void>;
60
+ }
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Factory
64
+ // ---------------------------------------------------------------------------
65
+
66
+ export function createExtensionPack(context: ExtensionContext): ExtensionPack {
67
+ const opts = (context.options ?? {}) as CloudflarePagesOptions;
68
+ const secrets = opts.secrets ?? context.secrets ?? {};
69
+ const config = resolveConfig(opts, secrets);
70
+
71
+ const service = new CloudflareService(config);
72
+
73
+ const deployTool = new CfDeployPagesTool(service);
74
+ const listProjectsTool = new CfListProjectsTool(service);
75
+ const configureDnsTool = new CfConfigureDnsTool(service);
76
+ const createWorkerTool = new CfCreateWorkerTool(service);
77
+
78
+ return {
79
+ name: '@framers/agentos-ext-cloud-cloudflare-pages',
80
+ version: '0.1.0',
81
+ descriptors: [
82
+ { id: 'cfDeployPages', kind: 'tool', priority: 40, payload: deployTool },
83
+ { id: 'cfListProjects', kind: 'tool', priority: 40, payload: listProjectsTool },
84
+ { id: 'cfConfigureDns', kind: 'tool', priority: 40, payload: configureDnsTool },
85
+ { id: 'cfCreateWorker', kind: 'tool', priority: 40, payload: createWorkerTool },
86
+ ],
87
+ onActivate: async () => {
88
+ if (!config.apiToken) {
89
+ throw new Error(
90
+ 'Cloudflare: no API token provided. Set CLOUDFLARE_API_TOKEN environment variable, '
91
+ + 'or provide it via secrets["cloudflare.apiToken"].',
92
+ );
93
+ }
94
+ if (!config.accountId) {
95
+ throw new Error(
96
+ 'Cloudflare: no Account ID provided. Set CLOUDFLARE_ACCOUNT_ID environment variable, '
97
+ + 'or provide it via secrets["cloudflare.accountId"].',
98
+ );
99
+ }
100
+ await service.initialize();
101
+ },
102
+ onDeactivate: async () => {
103
+ await service.shutdown();
104
+ },
105
+ };
106
+ }
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Public exports
110
+ // ---------------------------------------------------------------------------
111
+
112
+ export { CloudflareService } from './CloudflareService.js';
113
+ export type {
114
+ CloudflareConfig,
115
+ CloudflarePagesProject,
116
+ CloudflareDeployment,
117
+ CloudflareDnsRecord,
118
+ CloudflareZone,
119
+ CloudflareWorker,
120
+ DeployPagesResult,
121
+ CreatePagesProjectOptions,
122
+ DeployWorkerOptions,
123
+ } from './CloudflareService.js';
124
+ export { CfDeployPagesTool } from './tools/deploy.js';
125
+ export { CfListProjectsTool } from './tools/listProjects.js';
126
+ export { CfConfigureDnsTool } from './tools/configureDns.js';
127
+ export { CfCreateWorkerTool } from './tools/createWorker.js';
128
+
129
+ export default createExtensionPack;
@@ -0,0 +1,130 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @fileoverview CfConfigureDnsTool — manage DNS records on a Cloudflare zone.
4
+ */
5
+
6
+ import type { CloudflareService, CloudflareDnsRecord } from '../CloudflareService.js';
7
+
8
+ export interface ConfigureDnsInput {
9
+ domain: string;
10
+ action?: 'add' | 'remove' | 'list' | 'update';
11
+ recordType?: 'A' | 'AAAA' | 'CNAME' | 'TXT' | 'MX';
12
+ name?: string;
13
+ content?: string;
14
+ ttl?: number;
15
+ proxied?: boolean;
16
+ priority?: number;
17
+ comment?: string;
18
+ recordId?: string;
19
+ }
20
+
21
+ export class CfConfigureDnsTool {
22
+ readonly id = 'cfConfigureDns';
23
+ readonly name = 'cfConfigureDns';
24
+ readonly displayName = 'Configure DNS Records';
25
+ readonly description = 'Manage DNS records (A, AAAA, CNAME, TXT, MX) on a Cloudflare zone. Supports adding, removing, listing, and updating records. Automatically looks up the zone ID from the domain name.';
26
+ readonly category = 'cloud';
27
+ readonly version = '0.1.0';
28
+ readonly hasSideEffects = true;
29
+
30
+ readonly inputSchema = {
31
+ type: 'object' as const,
32
+ properties: {
33
+ domain: { type: 'string', description: 'Root domain for the Cloudflare zone (e.g. "example.com")' },
34
+ action: { type: 'string', enum: ['add', 'remove', 'list', 'update'], description: 'Action to perform (default: list)' },
35
+ recordType: { type: 'string', enum: ['A', 'AAAA', 'CNAME', 'TXT', 'MX'], description: 'DNS record type (required for add/update)' },
36
+ name: { type: 'string', description: 'Record name / hostname (e.g. "www", "@", "api.example.com")' },
37
+ content: { type: 'string', description: 'Record value (IP address, hostname, or text content)' },
38
+ ttl: { type: 'number', description: 'TTL in seconds (1 = auto, default: 1)' },
39
+ proxied: { type: 'boolean', description: 'Whether to proxy through Cloudflare (default: false)' },
40
+ priority: { type: 'number', description: 'Priority for MX records' },
41
+ comment: { type: 'string', description: 'Optional comment for the record' },
42
+ recordId: { type: 'string', description: 'Record ID (required for update/remove actions)' },
43
+ },
44
+ required: ['domain'],
45
+ };
46
+
47
+ constructor(private service: CloudflareService) {}
48
+
49
+ async execute(args: ConfigureDnsInput): Promise<{
50
+ success: boolean;
51
+ data?: CloudflareDnsRecord | CloudflareDnsRecord[] | { message: string };
52
+ error?: string;
53
+ }> {
54
+ try {
55
+ const action = args.action ?? 'list';
56
+
57
+ // Look up zone ID from domain
58
+ const zone = await this.service.getZoneByDomain(args.domain);
59
+
60
+ switch (action) {
61
+ case 'add': {
62
+ if (!args.recordType || !args.name || !args.content) {
63
+ return {
64
+ success: false,
65
+ error: 'recordType, name, and content are required for the "add" action.',
66
+ };
67
+ }
68
+
69
+ const record = await this.service.createDnsRecord(zone.id, {
70
+ type: args.recordType,
71
+ name: args.name,
72
+ content: args.content,
73
+ ttl: args.ttl,
74
+ proxied: args.proxied,
75
+ priority: args.priority,
76
+ comment: args.comment,
77
+ });
78
+
79
+ return { success: true, data: record };
80
+ }
81
+
82
+ case 'remove': {
83
+ if (!args.recordId) {
84
+ return {
85
+ success: false,
86
+ error: 'recordId is required for the "remove" action. Use action "list" first to find the record ID.',
87
+ };
88
+ }
89
+
90
+ await this.service.deleteDnsRecord(zone.id, args.recordId);
91
+ return { success: true, data: { message: `DNS record ${args.recordId} deleted.` } };
92
+ }
93
+
94
+ case 'update': {
95
+ if (!args.recordId) {
96
+ return {
97
+ success: false,
98
+ error: 'recordId is required for the "update" action. Use action "list" first to find the record ID.',
99
+ };
100
+ }
101
+
102
+ const updatePayload: Record<string, unknown> = {};
103
+ if (args.recordType) updatePayload.type = args.recordType;
104
+ if (args.name) updatePayload.name = args.name;
105
+ if (args.content) updatePayload.content = args.content;
106
+ if (args.ttl !== undefined) updatePayload.ttl = args.ttl;
107
+ if (args.proxied !== undefined) updatePayload.proxied = args.proxied;
108
+ if (args.priority !== undefined) updatePayload.priority = args.priority;
109
+ if (args.comment !== undefined) updatePayload.comment = args.comment;
110
+
111
+ const updated = await this.service.updateDnsRecord(zone.id, args.recordId, updatePayload);
112
+ return { success: true, data: updated };
113
+ }
114
+
115
+ case 'list': {
116
+ const records = await this.service.listDnsRecords(zone.id, {
117
+ type: args.recordType,
118
+ name: args.name,
119
+ });
120
+ return { success: true, data: records };
121
+ }
122
+
123
+ default:
124
+ return { success: false, error: `Unknown action: ${action}` };
125
+ }
126
+ } catch (err: any) {
127
+ return { success: false, error: err.message };
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,58 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @fileoverview CfCreateWorkerTool — deploy a Cloudflare Worker script.
4
+ */
5
+
6
+ import type { CloudflareService, CloudflareWorker } from '../CloudflareService.js';
7
+
8
+ export interface CreateWorkerInput {
9
+ name: string;
10
+ script: string;
11
+ routes?: string[];
12
+ compatibilityDate?: string;
13
+ bindings?: Record<string, string>;
14
+ }
15
+
16
+ export class CfCreateWorkerTool {
17
+ readonly id = 'cfCreateWorker';
18
+ readonly name = 'cfCreateWorker';
19
+ readonly displayName = 'Deploy Cloudflare Worker';
20
+ readonly description = 'Deploy a Cloudflare Worker script to the edge. Supports ES modules format, route patterns, compatibility dates, and environment variable bindings. Workers run on Cloudflare\'s global network with sub-millisecond cold starts.';
21
+ readonly category = 'cloud';
22
+ readonly version = '0.1.0';
23
+ readonly hasSideEffects = true;
24
+
25
+ readonly inputSchema = {
26
+ type: 'object' as const,
27
+ properties: {
28
+ name: { type: 'string', description: 'Worker script name (used as the identifier, e.g. "my-api-worker")' },
29
+ script: { type: 'string', description: 'JavaScript or TypeScript source code for the Worker (ES modules format with default export)' },
30
+ routes: {
31
+ type: 'array',
32
+ items: { type: 'string' },
33
+ description: 'Route patterns to bind the Worker to (e.g. ["example.com/api/*", "*.example.com/webhook"])',
34
+ },
35
+ compatibilityDate: { type: 'string', description: 'Workers runtime compatibility date (e.g. "2024-01-01", default: "2024-01-01")' },
36
+ bindings: { type: 'object', description: 'Environment variable bindings as key-value pairs' },
37
+ },
38
+ required: ['name', 'script'],
39
+ };
40
+
41
+ constructor(private service: CloudflareService) {}
42
+
43
+ async execute(args: CreateWorkerInput): Promise<{ success: boolean; data?: CloudflareWorker; error?: string }> {
44
+ try {
45
+ const worker = await this.service.deployWorker({
46
+ name: args.name,
47
+ script: args.script,
48
+ routes: args.routes,
49
+ compatibilityDate: args.compatibilityDate,
50
+ bindings: args.bindings,
51
+ });
52
+
53
+ return { success: true, data: worker };
54
+ } catch (err: any) {
55
+ return { success: false, error: err.message };
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,56 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @fileoverview CfDeployPagesTool — deploy a Cloudflare Pages project from a Git repository.
4
+ */
5
+
6
+ import type { CloudflareService, DeployPagesResult } from '../CloudflareService.js';
7
+
8
+ export interface CfDeployPagesInput {
9
+ gitUrl: string;
10
+ projectName?: string;
11
+ productionBranch?: string;
12
+ buildCommand?: string;
13
+ buildOutputDirectory?: string;
14
+ envVars?: Record<string, string>;
15
+ }
16
+
17
+ export class CfDeployPagesTool {
18
+ readonly id = 'cfDeployPages';
19
+ readonly name = 'cfDeployPages';
20
+ readonly displayName = 'Deploy Cloudflare Pages';
21
+ readonly description = 'Deploy a project to Cloudflare Pages from a Git repository URL. Creates the project if it does not exist. Supports static sites, Next.js, Astro, SvelteKit, Remix, and other full-stack frameworks with automatic build configuration.';
22
+ readonly category = 'cloud';
23
+ readonly version = '0.1.0';
24
+ readonly hasSideEffects = true;
25
+
26
+ readonly inputSchema = {
27
+ type: 'object' as const,
28
+ properties: {
29
+ gitUrl: { type: 'string', description: 'Git repository URL (e.g. "https://github.com/user/repo")' },
30
+ projectName: { type: 'string', description: 'Pages project name (auto-generated from repo name if omitted)' },
31
+ productionBranch: { type: 'string', description: 'Production branch name (default: "main")' },
32
+ buildCommand: { type: 'string', description: 'Build command override (e.g. "npm run build")' },
33
+ buildOutputDirectory: { type: 'string', description: 'Build output directory (e.g. "dist", "build", ".next")' },
34
+ envVars: { type: 'object', description: 'Environment variables to set on the project (key-value pairs)' },
35
+ },
36
+ required: ['gitUrl'],
37
+ };
38
+
39
+ constructor(private service: CloudflareService) {}
40
+
41
+ async execute(args: CfDeployPagesInput): Promise<{ success: boolean; data?: DeployPagesResult; error?: string }> {
42
+ try {
43
+ const result = await this.service.deployFromGit({
44
+ gitUrl: args.gitUrl,
45
+ productionBranch: args.productionBranch,
46
+ buildCommand: args.buildCommand,
47
+ buildOutputDirectory: args.buildOutputDirectory,
48
+ envVars: args.envVars,
49
+ });
50
+
51
+ return { success: true, data: result };
52
+ } catch (err: any) {
53
+ return { success: false, error: err.message };
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,35 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @fileoverview CfListProjectsTool — list all Cloudflare Pages projects.
4
+ */
5
+
6
+ import type { CloudflareService, CloudflarePagesProject } from '../CloudflareService.js';
7
+
8
+ export class CfListProjectsTool {
9
+ readonly id = 'cfListProjects';
10
+ readonly name = 'cfListProjects';
11
+ readonly displayName = 'List Pages Projects';
12
+ readonly description = 'List all Cloudflare Pages projects in the connected account. Shows project name, subdomain, production branch, linked Git repo, and latest deployment status.';
13
+ readonly category = 'cloud';
14
+ readonly version = '0.1.0';
15
+ readonly hasSideEffects = false;
16
+
17
+ readonly inputSchema = {
18
+ type: 'object' as const,
19
+ properties: {
20
+ limit: { type: 'number', description: 'Maximum number of projects to return (default: 25, max: 100)' },
21
+ },
22
+ required: [] as string[],
23
+ };
24
+
25
+ constructor(private service: CloudflareService) {}
26
+
27
+ async execute(args: { limit?: number }): Promise<{ success: boolean; data?: CloudflarePagesProject[]; error?: string }> {
28
+ try {
29
+ const projects = await this.service.listProjects(args.limit ?? 25);
30
+ return { success: true, data: projects };
31
+ } catch (err: any) {
32
+ return { success: false, error: err.message };
33
+ }
34
+ }
35
+ }