@framers/agentos-ext-cloud-digitalocean 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts DELETED
@@ -1,130 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview DigitalOcean Cloud Extension for AgentOS.
4
- *
5
- * Provides 6 tools for managing App Platform apps, Droplets, DNS records,
6
- * and deployments via the DigitalOcean REST API v2.
7
- *
8
- * @module @framers/agentos-ext-cloud-digitalocean
9
- */
10
-
11
- import { DigitalOceanService } from './DigitalOceanService.js';
12
- import type { DigitalOceanConfig } from './DigitalOceanService.js';
13
- import { DOCreateAppTool } from './tools/createApp.js';
14
- import { DOCreateDropletTool } from './tools/createDroplet.js';
15
- import { DOListResourcesTool } from './tools/listResources.js';
16
- import { DODeployAppTool } from './tools/deployApp.js';
17
- import { DOManageDnsTool } from './tools/manageDns.js';
18
- import { DODeleteResourceTool } from './tools/deleteResource.js';
19
-
20
- // ---------------------------------------------------------------------------
21
- // Options
22
- // ---------------------------------------------------------------------------
23
-
24
- export interface DigitalOceanCloudOptions {
25
- token?: string;
26
- secrets?: Record<string, string>;
27
- }
28
-
29
- // ---------------------------------------------------------------------------
30
- // Secret Resolution
31
- // ---------------------------------------------------------------------------
32
-
33
- function resolveConfig(opts: DigitalOceanCloudOptions, secrets: Record<string, string>): DigitalOceanConfig {
34
- return {
35
- token:
36
- opts.token ?? secrets['digitalocean.token']
37
- ?? process.env.DIGITALOCEAN_TOKEN ?? process.env.DO_API_TOKEN ?? '',
38
- };
39
- }
40
-
41
- // ---------------------------------------------------------------------------
42
- // Extension Context
43
- // ---------------------------------------------------------------------------
44
-
45
- export interface ExtensionContext {
46
- options?: Record<string, unknown>;
47
- secrets?: Record<string, string>;
48
- getSecret?: (key: string) => string | undefined;
49
- logger?: { info: (msg: string) => void };
50
- }
51
-
52
- export interface ExtensionPack {
53
- name: string;
54
- version: string;
55
- descriptors: Array<{ id: string; kind: string; priority?: number; payload: unknown }>;
56
- onActivate?: () => Promise<void>;
57
- onDeactivate?: () => Promise<void>;
58
- }
59
-
60
- // ---------------------------------------------------------------------------
61
- // Factory
62
- // ---------------------------------------------------------------------------
63
-
64
- export function createExtensionPack(context: ExtensionContext): ExtensionPack {
65
- const opts = (context.options ?? {}) as DigitalOceanCloudOptions;
66
- const secrets = opts.secrets ?? context.secrets ?? {};
67
- const config = resolveConfig(opts, secrets);
68
-
69
- const service = new DigitalOceanService(config);
70
-
71
- const createAppTool = new DOCreateAppTool(service);
72
- const createDropletTool = new DOCreateDropletTool(service);
73
- const listResourcesTool = new DOListResourcesTool(service);
74
- const deployAppTool = new DODeployAppTool(service);
75
- const manageDnsTool = new DOManageDnsTool(service);
76
- const deleteResourceTool = new DODeleteResourceTool(service);
77
-
78
- return {
79
- name: '@framers/agentos-ext-cloud-digitalocean',
80
- version: '0.1.0',
81
- descriptors: [
82
- { id: 'doCreateApp', kind: 'tool', priority: 40, payload: createAppTool },
83
- { id: 'doCreateDroplet', kind: 'tool', priority: 40, payload: createDropletTool },
84
- { id: 'doListResources', kind: 'tool', priority: 40, payload: listResourcesTool },
85
- { id: 'doDeployApp', kind: 'tool', priority: 40, payload: deployAppTool },
86
- { id: 'doManageDns', kind: 'tool', priority: 40, payload: manageDnsTool },
87
- { id: 'doDeleteResource', kind: 'tool', priority: 40, payload: deleteResourceTool },
88
- ],
89
- onActivate: async () => {
90
- if (!config.token) {
91
- throw new Error(
92
- 'DigitalOcean: no API token provided. Set DIGITALOCEAN_TOKEN or DO_API_TOKEN environment variable, '
93
- + 'or provide it via secrets["digitalocean.token"].',
94
- );
95
- }
96
- await service.initialize();
97
- },
98
- onDeactivate: async () => {
99
- await service.shutdown();
100
- },
101
- };
102
- }
103
-
104
- // ---------------------------------------------------------------------------
105
- // Public exports
106
- // ---------------------------------------------------------------------------
107
-
108
- export { DigitalOceanService } from './DigitalOceanService.js';
109
- export type {
110
- DigitalOceanConfig,
111
- DOApp,
112
- DOAppSpec,
113
- DOAppServiceSpec,
114
- DOAppStaticSiteSpec,
115
- DOAppEnvVar,
116
- DODeployment,
117
- DODroplet,
118
- DODomain,
119
- DODomainRecord,
120
- CreateAppOptions,
121
- CreateDropletOptions,
122
- } from './DigitalOceanService.js';
123
- export { DOCreateAppTool } from './tools/createApp.js';
124
- export { DOCreateDropletTool } from './tools/createDroplet.js';
125
- export { DOListResourcesTool } from './tools/listResources.js';
126
- export { DODeployAppTool } from './tools/deployApp.js';
127
- export { DOManageDnsTool } from './tools/manageDns.js';
128
- export { DODeleteResourceTool } from './tools/deleteResource.js';
129
-
130
- export default createExtensionPack;
@@ -1,69 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview DOCreateAppTool — create an App Platform app from a Git repository.
4
- */
5
-
6
- import type { DigitalOceanService, DOApp } from '../DigitalOceanService.js';
7
-
8
- export interface CreateAppInput {
9
- name: string;
10
- gitUrl: string;
11
- branch?: string;
12
- region?: string;
13
- buildCommand?: string;
14
- runCommand?: string;
15
- outputDir?: string;
16
- isStatic?: boolean;
17
- envVars?: Record<string, string>;
18
- instanceSizeSlug?: string;
19
- }
20
-
21
- export class DOCreateAppTool {
22
- readonly id = 'doCreateApp';
23
- readonly name = 'doCreateApp';
24
- readonly displayName = 'Create DO App';
25
- readonly description = 'Create a DigitalOcean App Platform app from a Git repository with auto-detected settings. Supports both services (Node.js, Python, Go, etc.) and static sites. Configures build/run commands, environment variables, region, and instance size.';
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
- name: { type: 'string', description: 'App name (must be lowercase, alphanumeric, and hyphens only)' },
34
- gitUrl: { type: 'string', description: 'Git repository clone URL (e.g. "https://github.com/user/repo")' },
35
- branch: { type: 'string', description: 'Git branch to deploy from (default: "main")' },
36
- region: { type: 'string', description: 'Region slug (e.g. "nyc", "sfo", "ams", "sgp"). Default: "nyc"' },
37
- buildCommand: { type: 'string', description: 'Build command override (e.g. "npm run build")' },
38
- runCommand: { type: 'string', description: 'Run command for services (e.g. "npm start")' },
39
- outputDir: { type: 'string', description: 'Output directory for static sites (e.g. "dist", "build")' },
40
- isStatic: { type: 'boolean', description: 'Whether to deploy as a static site (default: false, deploys as service)' },
41
- envVars: { type: 'object', description: 'Environment variables as key-value pairs' },
42
- instanceSizeSlug: { type: 'string', description: 'Instance size slug (e.g. "basic-xxs", "basic-xs", "professional-xs")' },
43
- },
44
- required: ['name', 'gitUrl'],
45
- };
46
-
47
- constructor(private service: DigitalOceanService) {}
48
-
49
- async execute(args: CreateAppInput): Promise<{ success: boolean; data?: DOApp; error?: string }> {
50
- try {
51
- const app = await this.service.createApp({
52
- name: args.name,
53
- gitUrl: args.gitUrl,
54
- branch: args.branch,
55
- region: args.region,
56
- buildCommand: args.buildCommand,
57
- runCommand: args.runCommand,
58
- outputDir: args.outputDir,
59
- isStatic: args.isStatic,
60
- envVars: args.envVars,
61
- instanceSizeSlug: args.instanceSizeSlug,
62
- });
63
-
64
- return { success: true, data: app };
65
- } catch (err: any) {
66
- return { success: false, error: err.message };
67
- }
68
- }
69
- }
@@ -1,74 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview DOCreateDropletTool — create a VPS Droplet with specified region, size, and image.
4
- */
5
-
6
- import type { DigitalOceanService, DODroplet } from '../DigitalOceanService.js';
7
-
8
- export interface CreateDropletInput {
9
- name: string;
10
- region: string;
11
- size: string;
12
- image: string;
13
- sshKeys?: (string | number)[];
14
- userData?: string;
15
- backups?: boolean;
16
- ipv6?: boolean;
17
- tags?: string[];
18
- }
19
-
20
- export class DOCreateDropletTool {
21
- readonly id = 'doCreateDroplet';
22
- readonly name = 'doCreateDroplet';
23
- readonly displayName = 'Create DO Droplet';
24
- readonly description = 'Create a DigitalOcean Droplet (VPS instance) with a specified region, size, and OS image. Supports SSH key injection, cloud-init user data scripts, backups, IPv6, and tagging.';
25
- readonly category = 'cloud';
26
- readonly version = '0.1.0';
27
- readonly hasSideEffects = true;
28
-
29
- readonly inputSchema = {
30
- type: 'object' as const,
31
- properties: {
32
- name: { type: 'string', description: 'Droplet hostname (e.g. "my-server-01")' },
33
- region: { type: 'string', description: 'Region slug (e.g. "nyc1", "sfo3", "ams3", "sgp1", "lon1")' },
34
- size: { type: 'string', description: 'Size slug (e.g. "s-1vcpu-1gb", "s-2vcpu-4gb", "s-4vcpu-8gb")' },
35
- image: { type: 'string', description: 'Image slug or ID (e.g. "ubuntu-24-04-x64", "debian-12-x64", "docker-20-04")' },
36
- sshKeys: {
37
- type: 'array',
38
- items: { type: ['string', 'number'] },
39
- description: 'SSH key IDs or fingerprints to embed in the Droplet',
40
- },
41
- userData: { type: 'string', description: 'Cloud-init user data script for automated setup' },
42
- backups: { type: 'boolean', description: 'Enable weekly backups (default: false)' },
43
- ipv6: { type: 'boolean', description: 'Enable IPv6 networking (default: false)' },
44
- tags: {
45
- type: 'array',
46
- items: { type: 'string' },
47
- description: 'Tags to apply to the Droplet for organization and filtering',
48
- },
49
- },
50
- required: ['name', 'region', 'size', 'image'],
51
- };
52
-
53
- constructor(private service: DigitalOceanService) {}
54
-
55
- async execute(args: CreateDropletInput): Promise<{ success: boolean; data?: DODroplet; error?: string }> {
56
- try {
57
- const droplet = await this.service.createDroplet({
58
- name: args.name,
59
- region: args.region,
60
- size: args.size,
61
- image: args.image,
62
- sshKeys: args.sshKeys,
63
- userData: args.userData,
64
- backups: args.backups,
65
- ipv6: args.ipv6,
66
- tags: args.tags,
67
- });
68
-
69
- return { success: true, data: droplet };
70
- } catch (err: any) {
71
- return { success: false, error: err.message };
72
- }
73
- }
74
- }
@@ -1,64 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview DODeleteResourceTool — delete an app or droplet from DigitalOcean.
4
- */
5
-
6
- import type { DigitalOceanService } from '../DigitalOceanService.js';
7
-
8
- export interface DeleteResourceInput {
9
- resourceType: 'app' | 'droplet';
10
- resourceId: string;
11
- }
12
-
13
- export class DODeleteResourceTool {
14
- readonly id = 'doDeleteResource';
15
- readonly name = 'doDeleteResource';
16
- readonly displayName = 'Delete DO Resource';
17
- readonly description = 'Permanently delete a DigitalOcean App Platform app or Droplet. This action is irreversible — the resource and all associated data will be destroyed.';
18
- readonly category = 'cloud';
19
- readonly version = '0.1.0';
20
- readonly hasSideEffects = true;
21
-
22
- readonly inputSchema = {
23
- type: 'object' as const,
24
- properties: {
25
- resourceType: {
26
- type: 'string',
27
- enum: ['app', 'droplet'],
28
- description: 'Type of resource to delete',
29
- },
30
- resourceId: {
31
- type: 'string',
32
- description: 'Resource ID to delete (app UUID or droplet numeric ID)',
33
- },
34
- },
35
- required: ['resourceType', 'resourceId'],
36
- };
37
-
38
- constructor(private service: DigitalOceanService) {}
39
-
40
- async execute(args: DeleteResourceInput): Promise<{ success: boolean; data?: { message: string }; error?: string }> {
41
- try {
42
- switch (args.resourceType) {
43
- case 'app': {
44
- await this.service.deleteApp(args.resourceId);
45
- return { success: true, data: { message: `App ${args.resourceId} has been deleted.` } };
46
- }
47
-
48
- case 'droplet': {
49
- const dropletId = parseInt(args.resourceId, 10);
50
- if (isNaN(dropletId)) {
51
- return { success: false, error: `Invalid droplet ID: "${args.resourceId}". Must be a numeric ID.` };
52
- }
53
- await this.service.deleteDroplet(dropletId);
54
- return { success: true, data: { message: `Droplet ${dropletId} has been deleted.` } };
55
- }
56
-
57
- default:
58
- return { success: false, error: `Unknown resource type: ${args.resourceType}. Use "app" or "droplet".` };
59
- }
60
- } catch (err: any) {
61
- return { success: false, error: err.message };
62
- }
63
- }
64
- }
@@ -1,41 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview DODeployAppTool — trigger a new deployment on an existing App Platform app.
4
- */
5
-
6
- import type { DigitalOceanService, DODeployment } from '../DigitalOceanService.js';
7
-
8
- export interface DeployAppInput {
9
- appId: string;
10
- forceBuild?: boolean;
11
- }
12
-
13
- export class DODeployAppTool {
14
- readonly id = 'doDeployApp';
15
- readonly name = 'doDeployApp';
16
- readonly displayName = 'Deploy DO App';
17
- readonly description = 'Trigger a new deployment on an existing DigitalOcean App Platform app. Pulls the latest code from the configured Git repository and builds/deploys it. Optionally force a full rebuild.';
18
- readonly category = 'cloud';
19
- readonly version = '0.1.0';
20
- readonly hasSideEffects = true;
21
-
22
- readonly inputSchema = {
23
- type: 'object' as const,
24
- properties: {
25
- appId: { type: 'string', description: 'App ID to deploy (use doListResources to find app IDs)' },
26
- forceBuild: { type: 'boolean', description: 'Force a complete rebuild instead of incremental (default: false)' },
27
- },
28
- required: ['appId'],
29
- };
30
-
31
- constructor(private service: DigitalOceanService) {}
32
-
33
- async execute(args: DeployAppInput): Promise<{ success: boolean; data?: DODeployment; error?: string }> {
34
- try {
35
- const deployment = await this.service.createDeployment(args.appId, args.forceBuild);
36
- return { success: true, data: deployment };
37
- } catch (err: any) {
38
- return { success: false, error: err.message };
39
- }
40
- }
41
- }
@@ -1,66 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview DOListResourcesTool — list all apps and/or droplets on a DigitalOcean account.
4
- */
5
-
6
- import type { DigitalOceanService, DOApp, DODroplet } from '../DigitalOceanService.js';
7
-
8
- export interface ListResourcesInput {
9
- resourceType?: 'apps' | 'droplets' | 'all';
10
- page?: number;
11
- perPage?: number;
12
- tag?: string;
13
- }
14
-
15
- export interface ListResourcesResult {
16
- apps?: DOApp[];
17
- droplets?: DODroplet[];
18
- }
19
-
20
- export class DOListResourcesTool {
21
- readonly id = 'doListResources';
22
- readonly name = 'doListResources';
23
- readonly displayName = 'List DO Resources';
24
- readonly description = 'List all DigitalOcean App Platform apps and/or Droplets. Filter by resource type (apps, droplets, or both) and optionally by tag. Returns names, statuses, regions, IPs, and deployment info.';
25
- readonly category = 'cloud';
26
- readonly version = '0.1.0';
27
- readonly hasSideEffects = false;
28
-
29
- readonly inputSchema = {
30
- type: 'object' as const,
31
- properties: {
32
- resourceType: {
33
- type: 'string',
34
- enum: ['apps', 'droplets', 'all'],
35
- description: 'Which resource type to list (default: "all")',
36
- },
37
- page: { type: 'number', description: 'Page number for pagination (default: 1)' },
38
- perPage: { type: 'number', description: 'Items per page (default: 20, max: 200)' },
39
- tag: { type: 'string', description: 'Filter droplets by tag name' },
40
- },
41
- required: [] as string[],
42
- };
43
-
44
- constructor(private service: DigitalOceanService) {}
45
-
46
- async execute(args: ListResourcesInput): Promise<{ success: boolean; data?: ListResourcesResult; error?: string }> {
47
- try {
48
- const resourceType = args.resourceType ?? 'all';
49
- const page = args.page ?? 1;
50
- const perPage = args.perPage ?? 20;
51
- const result: ListResourcesResult = {};
52
-
53
- if (resourceType === 'apps' || resourceType === 'all') {
54
- result.apps = await this.service.listApps(page, perPage);
55
- }
56
-
57
- if (resourceType === 'droplets' || resourceType === 'all') {
58
- result.droplets = await this.service.listDroplets(page, perPage, args.tag);
59
- }
60
-
61
- return { success: true, data: result };
62
- } catch (err: any) {
63
- return { success: false, error: err.message };
64
- }
65
- }
66
- }
@@ -1,131 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview DOManageDnsTool — manage DNS records on a DigitalOcean domain.
4
- */
5
-
6
- import type { DigitalOceanService, DODomain, DODomainRecord } from '../DigitalOceanService.js';
7
-
8
- export interface ManageDnsInput {
9
- action: 'list-domains' | 'add-domain' | 'list' | 'add' | 'update' | 'delete';
10
- domain?: string;
11
- recordId?: number;
12
- recordType?: string;
13
- name?: string;
14
- data?: string;
15
- ttl?: number;
16
- priority?: number;
17
- port?: number;
18
- weight?: number;
19
- }
20
-
21
- export class DOManageDnsTool {
22
- readonly id = 'doManageDns';
23
- readonly name = 'doManageDns';
24
- readonly displayName = 'Manage DO DNS';
25
- readonly description = 'Manage DNS records on a DigitalOcean domain. Supports listing domains, adding domains, and full CRUD on DNS records (A, AAAA, CNAME, MX, TXT, SRV, NS, CAA). Use action "list-domains" to see all domains, "add-domain" to register a new domain, "list" to view records, "add"/"update"/"delete" to manage records.';
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
- action: {
34
- type: 'string',
35
- enum: ['list-domains', 'add-domain', 'list', 'add', 'update', 'delete'],
36
- description: 'Action to perform on DNS',
37
- },
38
- domain: { type: 'string', description: 'Domain name (e.g. "example.com"). Required for all actions except "list-domains".' },
39
- recordId: { type: 'number', description: 'Record ID (required for "update" and "delete" actions)' },
40
- recordType: { type: 'string', description: 'DNS record type (e.g. "A", "AAAA", "CNAME", "MX", "TXT", "SRV", "NS", "CAA")' },
41
- name: { type: 'string', description: 'Record name/hostname (e.g. "@", "www", "mail")' },
42
- data: { type: 'string', description: 'Record data/value (e.g. IP address, CNAME target, MX server)' },
43
- ttl: { type: 'number', description: 'Time-to-live in seconds (default: 1800)' },
44
- priority: { type: 'number', description: 'Priority (for MX and SRV records)' },
45
- port: { type: 'number', description: 'Port (for SRV records)' },
46
- weight: { type: 'number', description: 'Weight (for SRV records)' },
47
- },
48
- required: ['action'],
49
- };
50
-
51
- constructor(private service: DigitalOceanService) {}
52
-
53
- async execute(args: ManageDnsInput): Promise<{
54
- success: boolean;
55
- data?: DODomain | DODomain[] | DODomainRecord | DODomainRecord[] | { message: string };
56
- error?: string;
57
- }> {
58
- try {
59
- switch (args.action) {
60
- case 'list-domains': {
61
- const domains = await this.service.listDomains();
62
- return { success: true, data: domains };
63
- }
64
-
65
- case 'add-domain': {
66
- if (!args.domain) {
67
- return { success: false, error: 'Domain name is required for "add-domain" action.' };
68
- }
69
- const domain = await this.service.addDomain(args.domain);
70
- return { success: true, data: domain };
71
- }
72
-
73
- case 'list': {
74
- if (!args.domain) {
75
- return { success: false, error: 'Domain name is required for "list" action.' };
76
- }
77
- const records = await this.service.listDomainRecords(args.domain);
78
- return { success: true, data: records };
79
- }
80
-
81
- case 'add': {
82
- if (!args.domain) {
83
- return { success: false, error: 'Domain name is required for "add" action.' };
84
- }
85
- if (!args.recordType || !args.name || !args.data) {
86
- return { success: false, error: 'recordType, name, and data are required for "add" action.' };
87
- }
88
- const record = await this.service.createDomainRecord(args.domain, {
89
- type: args.recordType,
90
- name: args.name,
91
- data: args.data,
92
- ttl: args.ttl,
93
- priority: args.priority,
94
- port: args.port,
95
- weight: args.weight,
96
- });
97
- return { success: true, data: record };
98
- }
99
-
100
- case 'update': {
101
- if (!args.domain || args.recordId == null) {
102
- return { success: false, error: 'Domain name and recordId are required for "update" action.' };
103
- }
104
- const updated = await this.service.updateDomainRecord(args.domain, args.recordId, {
105
- type: args.recordType,
106
- name: args.name,
107
- data: args.data,
108
- ttl: args.ttl,
109
- priority: args.priority,
110
- port: args.port,
111
- weight: args.weight,
112
- });
113
- return { success: true, data: updated };
114
- }
115
-
116
- case 'delete': {
117
- if (!args.domain || args.recordId == null) {
118
- return { success: false, error: 'Domain name and recordId are required for "delete" action.' };
119
- }
120
- await this.service.deleteDomainRecord(args.domain, args.recordId);
121
- return { success: true, data: { message: `Record ${args.recordId} deleted from ${args.domain}.` } };
122
- }
123
-
124
- default:
125
- return { success: false, error: `Unknown action: ${args.action}` };
126
- }
127
- } catch (err: any) {
128
- return { success: false, error: err.message };
129
- }
130
- }
131
- }