@byfungsi/funforge 0.2.3 → 0.5.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.
@@ -124,19 +124,31 @@ describe("MCP Integration", () => {
124
124
  "funforge_whoami",
125
125
  "funforge_apps_list",
126
126
  "funforge_apps_create",
127
+ "funforge_apps_delete",
127
128
  "funforge_status",
128
129
  "funforge_deploy",
130
+ "funforge_stop",
131
+ "funforge_start",
132
+ "funforge_cancel",
129
133
  "funforge_env_list",
130
134
  "funforge_env_set",
131
135
  "funforge_env_unset",
132
136
  "funforge_domains_list",
133
137
  "funforge_domains_add",
138
+ "funforge_domains_remove",
139
+ "funforge_domains_verify",
134
140
  ];
135
141
  it("should define all expected tools", () => {
136
142
  // This is a documentation test to ensure we have all tools
137
- expect(expectedTools).toHaveLength(10);
143
+ expect(expectedTools).toHaveLength(16);
138
144
  expect(expectedTools).toContain("funforge_deploy");
139
145
  expect(expectedTools).toContain("funforge_whoami");
146
+ expect(expectedTools).toContain("funforge_stop");
147
+ expect(expectedTools).toContain("funforge_start");
148
+ expect(expectedTools).toContain("funforge_cancel");
149
+ expect(expectedTools).toContain("funforge_apps_delete");
150
+ expect(expectedTools).toContain("funforge_domains_remove");
151
+ expect(expectedTools).toContain("funforge_domains_verify");
140
152
  });
141
153
  });
142
154
  });
package/dist/api.d.ts CHANGED
@@ -60,6 +60,8 @@ export interface AppDetails extends App {
60
60
  startCommand?: string | null;
61
61
  /** Node.js version (18, 20, 22) */
62
62
  nodeVersion?: string | null;
63
+ /** Root directory for monorepo builds (e.g., "apps/web") */
64
+ rootDirectory?: string | null;
63
65
  }
64
66
  export interface ListAppsResponse {
65
67
  apps: App[];
@@ -92,6 +94,8 @@ export interface UpdateAppSettings {
92
94
  startCommand?: string | null;
93
95
  /** Node.js version (18, 20, 22) - null clears */
94
96
  nodeVersion?: "18" | "20" | "22" | null;
97
+ /** Root directory for monorepo builds (e.g., "apps/web") - null clears */
98
+ rootDirectory?: string | null;
95
99
  }
96
100
  /**
97
101
  * Update app settings (build commands, port, etc.)
@@ -154,5 +158,43 @@ export declare function getDeployment(deploymentId: string): Promise<{
154
158
  * Stream deployment logs (returns EventSource URL)
155
159
  */
156
160
  export declare function getDeploymentLogsUrl(deploymentId: string): string;
161
+ /**
162
+ * Stop the current deployment for an app
163
+ */
164
+ export declare function stopDeployment(appId: string): Promise<{
165
+ success: boolean;
166
+ }>;
167
+ /**
168
+ * Start a stopped deployment for an app
169
+ */
170
+ export declare function startDeployment(appId: string): Promise<{
171
+ success: boolean;
172
+ }>;
173
+ /**
174
+ * Cancel an in-progress deployment
175
+ */
176
+ export declare function cancelDeployment(appId: string): Promise<{
177
+ success: boolean;
178
+ }>;
179
+ /**
180
+ * Delete an app
181
+ */
182
+ export declare function deleteApp(appId: string): Promise<{
183
+ success: boolean;
184
+ }>;
185
+ /**
186
+ * Remove a custom domain from an app
187
+ */
188
+ export declare function removeDomain(appId: string, domainId: string): Promise<{
189
+ success: boolean;
190
+ }>;
191
+ /**
192
+ * Verify DNS and provision SSL for a domain
193
+ */
194
+ export declare function verifyDomain(appId: string, domainId: string): Promise<{
195
+ verified: boolean;
196
+ sslStatus?: string;
197
+ message?: string;
198
+ }>;
157
199
  export {};
158
200
  //# sourceMappingURL=api.d.ts.map
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/api.js CHANGED
@@ -208,3 +208,57 @@ export function getDeploymentLogsUrl(deploymentId) {
208
208
  const apiKey = getApiKey();
209
209
  return `${config.apiUrl}/api/cli/deployments/${deploymentId}/logs?token=${apiKey}`;
210
210
  }
211
+ // ============================================
212
+ // DEPLOYMENT CONTROL ENDPOINTS
213
+ // ============================================
214
+ /**
215
+ * Stop the current deployment for an app
216
+ */
217
+ export async function stopDeployment(appId) {
218
+ return apiRequest(`/api/cli/apps/${appId}/stop`, {
219
+ method: "POST",
220
+ });
221
+ }
222
+ /**
223
+ * Start a stopped deployment for an app
224
+ */
225
+ export async function startDeployment(appId) {
226
+ return apiRequest(`/api/cli/apps/${appId}/start`, {
227
+ method: "POST",
228
+ });
229
+ }
230
+ /**
231
+ * Cancel an in-progress deployment
232
+ */
233
+ export async function cancelDeployment(appId) {
234
+ return apiRequest(`/api/cli/apps/${appId}/cancel`, {
235
+ method: "POST",
236
+ });
237
+ }
238
+ /**
239
+ * Delete an app
240
+ */
241
+ export async function deleteApp(appId) {
242
+ return apiRequest(`/api/cli/apps/${appId}`, {
243
+ method: "DELETE",
244
+ });
245
+ }
246
+ // ============================================
247
+ // DOMAIN ENDPOINTS
248
+ // ============================================
249
+ /**
250
+ * Remove a custom domain from an app
251
+ */
252
+ export async function removeDomain(appId, domainId) {
253
+ return apiRequest(`/api/cli/apps/${appId}/domains/${domainId}`, {
254
+ method: "DELETE",
255
+ });
256
+ }
257
+ /**
258
+ * Verify DNS and provision SSL for a domain
259
+ */
260
+ export async function verifyDomain(appId, domainId) {
261
+ return apiRequest(`/api/cli/apps/${appId}/domains/${domainId}/verify`, {
262
+ method: "POST",
263
+ });
264
+ }
package/dist/cli.js CHANGED
@@ -10,10 +10,11 @@ import { Command } from "commander";
10
10
  // Load version from package.json
11
11
  const require = createRequire(import.meta.url);
12
12
  const pkg = require("../package.json");
13
- import { appsCreateCommand, appsListCommand, linkCommand, } from "./commands/apps.js";
13
+ import { appsCreateCommand, appsDeleteCommand, appsListCommand, linkCommand, } from "./commands/apps.js";
14
14
  // Import commands
15
15
  import { loginCommand, logoutCommand, whoamiCommand } from "./commands/auth.js";
16
16
  import { configPullCommand, configPushCommand, configShowCommand, } from "./commands/config.js";
17
+ import { cancelCommand, startCommand, stopCommand, } from "./commands/control.js";
17
18
  import { deployCommand } from "./commands/deploy.js";
18
19
  import { domainsAddCommand, domainsListCommand, domainsRemoveCommand, domainsVerifyCommand, } from "./commands/domains.js";
19
20
  import { envListCommand, envSetCommand, envUnsetCommand, } from "./commands/env.js";
@@ -49,6 +50,11 @@ appsCmd
49
50
  .option("-s, --slug <slug>", "Custom slug (subdomain)")
50
51
  .option("-t, --tier <tier>", "Tier key (e.g., cost-optimized.small)")
51
52
  .action(appsCreateCommand);
53
+ appsCmd
54
+ .command("delete <appId>")
55
+ .description("Delete an app")
56
+ .option("-y, --yes", "Skip confirmation prompt")
57
+ .action(appsDeleteCommand);
52
58
  program
53
59
  .command("link [appId]")
54
60
  .description("Link current directory to an app")
@@ -63,6 +69,21 @@ program
63
69
  .option("--no-watch", "Don't watch deployment logs")
64
70
  .action(deployCommand);
65
71
  // ============================================
72
+ // DEPLOYMENT CONTROL COMMANDS
73
+ // ============================================
74
+ program
75
+ .command("stop")
76
+ .description("Stop the current deployment")
77
+ .action(stopCommand);
78
+ program
79
+ .command("start")
80
+ .description("Start a stopped deployment")
81
+ .action(startCommand);
82
+ program
83
+ .command("cancel")
84
+ .description("Cancel an in-progress deployment")
85
+ .action(cancelCommand);
86
+ // ============================================
66
87
  // ENVIRONMENT VARIABLES
67
88
  // ============================================
68
89
  const envCmd = program
@@ -27,4 +27,12 @@ export declare function appsCreateCommand(name: string, options: {
27
27
  * If no appId provided, shows interactive selector.
28
28
  */
29
29
  export declare function linkCommand(appId?: string): Promise<void>;
30
+ /**
31
+ * funforge apps delete <appId>
32
+ *
33
+ * Delete an app (requires confirmation).
34
+ */
35
+ export declare function appsDeleteCommand(appId: string, options: {
36
+ yes?: boolean;
37
+ }): Promise<void>;
30
38
  //# sourceMappingURL=apps.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAgCrD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8D/D"}
1
+ {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgBH;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAgCrD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8D/D;AAaD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CA8Cf"}
@@ -5,9 +5,10 @@
5
5
  * - apps create: Create a new app (with interactive tier selection)
6
6
  * - link: Link current directory to an app
7
7
  */
8
+ import { confirm } from "@inquirer/prompts";
8
9
  import chalk from "chalk";
9
10
  import ora from "ora";
10
- import { createApp, getApp, listApps } from "../api.js";
11
+ import { createApp, deleteApp, getApp, listApps } from "../api.js";
11
12
  import { isAuthenticated } from "../credentials.js";
12
13
  import { handleError } from "../errors.js";
13
14
  import { readConfig, updateConfig } from "../project-config.js";
@@ -151,3 +152,50 @@ function requireAuth() {
151
152
  process.exit(1);
152
153
  }
153
154
  }
155
+ /**
156
+ * funforge apps delete <appId>
157
+ *
158
+ * Delete an app (requires confirmation).
159
+ */
160
+ export async function appsDeleteCommand(appId, options) {
161
+ requireAuth();
162
+ // Get app info first
163
+ const infoSpinner = ora("Fetching app info...").start();
164
+ let app;
165
+ try {
166
+ const result = await getApp(appId);
167
+ app = result.app;
168
+ infoSpinner.stop();
169
+ }
170
+ catch (error) {
171
+ infoSpinner.fail("Failed to fetch app");
172
+ handleError(error);
173
+ return;
174
+ }
175
+ console.log();
176
+ console.log(chalk.bold("App to delete:"));
177
+ console.log(` Name: ${app.name}`);
178
+ console.log(` Slug: ${chalk.cyan(app.slug)}`);
179
+ console.log(` ID: ${app.id}`);
180
+ console.log();
181
+ // Confirm deletion
182
+ if (!options.yes) {
183
+ const confirmed = await confirm({
184
+ message: `Are you sure you want to delete "${app.name}"? This cannot be undone.`,
185
+ default: false,
186
+ });
187
+ if (!confirmed) {
188
+ console.log(chalk.gray("Deletion canceled."));
189
+ return;
190
+ }
191
+ }
192
+ const deleteSpinner = ora(`Deleting ${app.name}...`).start();
193
+ try {
194
+ await deleteApp(appId);
195
+ deleteSpinner.succeed(`App "${app.name}" deleted`);
196
+ }
197
+ catch (error) {
198
+ deleteSpinner.fail("Failed to delete app");
199
+ handleError(error);
200
+ }
201
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoEH;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA+EvD;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA4EvD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgFvD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAyEH;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkFvD;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkFvD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAmFvD"}
@@ -22,6 +22,10 @@ function extractBuildSettings(config) {
22
22
  if (config.port !== undefined) {
23
23
  settings.port = config.port;
24
24
  }
25
+ // Root directory for monorepo builds
26
+ if (config.rootDirectory !== undefined) {
27
+ settings.rootDirectory = config.rootDirectory || null;
28
+ }
25
29
  if (config.build) {
26
30
  if (config.build.buildCommand !== undefined) {
27
31
  settings.buildCommand = config.build.buildCommand || null;
@@ -102,6 +106,9 @@ export async function configPushCommand() {
102
106
  if (settings.port !== undefined) {
103
107
  console.log(` Port: ${formatValue(settings.port)}`);
104
108
  }
109
+ if (settings.rootDirectory !== undefined) {
110
+ console.log(` Root directory: ${formatValue(settings.rootDirectory)}`);
111
+ }
105
112
  if (settings.buildCommand !== undefined) {
106
113
  console.log(` Build command: ${formatValue(settings.buildCommand)}`);
107
114
  }
@@ -158,6 +165,10 @@ export async function configPullCommand() {
158
165
  if (app.port !== undefined) {
159
166
  updatedConfig.port = app.port;
160
167
  }
168
+ // Sync root directory if set
169
+ if (app.rootDirectory) {
170
+ updatedConfig.rootDirectory = app.rootDirectory;
171
+ }
161
172
  // Build build settings object
162
173
  const build = {};
163
174
  let hasBuildSettings = false;
@@ -183,6 +194,7 @@ export async function configPullCommand() {
183
194
  // Show what will be written
184
195
  console.log(chalk.gray("Settings from server:"));
185
196
  console.log(` Port: ${formatValue(app.port)}`);
197
+ console.log(` Root directory: ${formatValue(app.rootDirectory)}`);
186
198
  console.log(` Build command: ${formatValue(app.buildCommand)}`);
187
199
  console.log(` Install command: ${formatValue(app.installCommand)}`);
188
200
  console.log(` Start command: ${formatValue(app.startCommand)}`);
@@ -220,12 +232,14 @@ export async function configShowCommand() {
220
232
  spinner.stop();
221
233
  // Local values
222
234
  const localPort = config.port;
235
+ const localRootDirectory = config.rootDirectory;
223
236
  const localBuildCommand = config.build?.buildCommand;
224
237
  const localInstallCommand = config.build?.installCommand;
225
238
  const localStartCommand = config.build?.startCommand;
226
239
  const localNodeVersion = config.build?.nodeVersion;
227
240
  // Server values
228
241
  const serverPort = app.port;
242
+ const serverRootDirectory = app.rootDirectory;
229
243
  const serverBuildCommand = app.buildCommand;
230
244
  const serverInstallCommand = app.installCommand;
231
245
  const serverStartCommand = app.startCommand;
@@ -244,6 +258,7 @@ export async function configShowCommand() {
244
258
  console.log(`${marker}${name.padEnd(19)}${localStr}${serverStr}`);
245
259
  };
246
260
  printRow("Port", localPort, serverPort);
261
+ printRow("Root directory", localRootDirectory, serverRootDirectory);
247
262
  printRow("Build command", localBuildCommand, serverBuildCommand);
248
263
  printRow("Install command", localInstallCommand, serverInstallCommand);
249
264
  printRow("Start command", localStartCommand, serverStartCommand);
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Deployment Control Commands
3
+ *
4
+ * - stop: Stop the current deployment
5
+ * - start: Start a stopped deployment
6
+ * - cancel: Cancel an in-progress deployment
7
+ */
8
+ /**
9
+ * funforge stop
10
+ *
11
+ * Stop the current deployment for the linked app.
12
+ */
13
+ export declare function stopCommand(): Promise<void>;
14
+ /**
15
+ * funforge start
16
+ *
17
+ * Start a stopped deployment for the linked app.
18
+ */
19
+ export declare function startCommand(): Promise<void>;
20
+ /**
21
+ * funforge cancel
22
+ *
23
+ * Cancel an in-progress deployment for the linked app.
24
+ */
25
+ export declare function cancelCommand(): Promise<void>;
26
+ //# sourceMappingURL=control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../../src/commands/control.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH;;;;GAIG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAejD;AAED;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAalD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAanD"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Deployment Control Commands
3
+ *
4
+ * - stop: Stop the current deployment
5
+ * - start: Start a stopped deployment
6
+ * - cancel: Cancel an in-progress deployment
7
+ */
8
+ import chalk from "chalk";
9
+ import ora from "ora";
10
+ import { cancelDeployment, startDeployment, stopDeployment } from "../api.js";
11
+ import { isAuthenticated } from "../credentials.js";
12
+ import { handleError } from "../errors.js";
13
+ import { readConfig } from "../project-config.js";
14
+ /**
15
+ * funforge stop
16
+ *
17
+ * Stop the current deployment for the linked app.
18
+ */
19
+ export async function stopCommand() {
20
+ requireAuth();
21
+ const appId = await getLinkedAppId();
22
+ const spinner = ora("Stopping deployment...").start();
23
+ try {
24
+ await stopDeployment(appId);
25
+ spinner.succeed("Deployment stopped");
26
+ console.log();
27
+ console.log(chalk.gray("Use `funforge start` to restart the deployment"));
28
+ }
29
+ catch (error) {
30
+ spinner.fail("Failed to stop deployment");
31
+ handleError(error);
32
+ }
33
+ }
34
+ /**
35
+ * funforge start
36
+ *
37
+ * Start a stopped deployment for the linked app.
38
+ */
39
+ export async function startCommand() {
40
+ requireAuth();
41
+ const appId = await getLinkedAppId();
42
+ const spinner = ora("Starting deployment...").start();
43
+ try {
44
+ await startDeployment(appId);
45
+ spinner.succeed("Deployment started");
46
+ }
47
+ catch (error) {
48
+ spinner.fail("Failed to start deployment");
49
+ handleError(error);
50
+ }
51
+ }
52
+ /**
53
+ * funforge cancel
54
+ *
55
+ * Cancel an in-progress deployment for the linked app.
56
+ */
57
+ export async function cancelCommand() {
58
+ requireAuth();
59
+ const appId = await getLinkedAppId();
60
+ const spinner = ora("Canceling deployment...").start();
61
+ try {
62
+ await cancelDeployment(appId);
63
+ spinner.succeed("Deployment canceled");
64
+ }
65
+ catch (error) {
66
+ spinner.fail("Failed to cancel deployment");
67
+ handleError(error);
68
+ }
69
+ }
70
+ /**
71
+ * Get the linked app ID or exit
72
+ */
73
+ async function getLinkedAppId() {
74
+ const config = await readConfig();
75
+ if (!config?.appId) {
76
+ console.log(chalk.red("Not linked to an app."));
77
+ console.log(chalk.gray("Run `funforge link <app-id>` first."));
78
+ process.exit(1);
79
+ }
80
+ return config.appId;
81
+ }
82
+ /**
83
+ * Check if user is authenticated, exit if not
84
+ */
85
+ function requireAuth() {
86
+ if (!isAuthenticated()) {
87
+ console.log(chalk.red("Not authenticated."));
88
+ console.log(chalk.gray("Run `funforge login` first."));
89
+ process.exit(1);
90
+ }
91
+ }
@@ -99,7 +99,7 @@ export async function deployCommand(options = {}) {
99
99
  console.log();
100
100
  // 6. Watch logs if requested
101
101
  if (options.watch !== false) {
102
- await watchDeployment(deployResult.deployment.id);
102
+ await watchDeployment(deployResult.deployment.id, config.appSlug);
103
103
  }
104
104
  else {
105
105
  console.log(chalk.gray("Run with --watch to follow logs"));
@@ -108,7 +108,7 @@ export async function deployCommand(options = {}) {
108
108
  /**
109
109
  * Watch deployment status until completion
110
110
  */
111
- async function watchDeployment(deploymentId) {
111
+ async function watchDeployment(deploymentId, appSlug) {
112
112
  console.log(chalk.gray("Watching deployment..."));
113
113
  console.log();
114
114
  const spinner = ora("Building...").start();
@@ -123,7 +123,12 @@ async function watchDeployment(deploymentId) {
123
123
  if (deployment.status === "live") {
124
124
  spinner.succeed(chalk.green("Deployment live!"));
125
125
  console.log();
126
- // TODO: Show deployment URL
126
+ // Show deployment URL
127
+ if (appSlug) {
128
+ console.log(chalk.bold("Your app is live at:"));
129
+ console.log(` ${chalk.cyan(`https://sarana-${appSlug}.funforge.app`)}`);
130
+ console.log();
131
+ }
127
132
  return;
128
133
  }
129
134
  if (deployment.status === "failed" || deployment.status === "canceled") {
package/dist/mcp.js CHANGED
@@ -195,6 +195,98 @@ const TOOLS = [
195
195
  required: ["domain"],
196
196
  },
197
197
  },
198
+ {
199
+ name: "funforge_domains_remove",
200
+ description: "Remove a custom domain from the linked app.",
201
+ inputSchema: {
202
+ type: "object",
203
+ properties: {
204
+ directory: {
205
+ type: "string",
206
+ description: "Directory with funforge.json (defaults to cwd)",
207
+ },
208
+ domain: {
209
+ type: "string",
210
+ description: "Domain name or domain ID to remove",
211
+ },
212
+ },
213
+ required: ["domain"],
214
+ },
215
+ },
216
+ {
217
+ name: "funforge_domains_verify",
218
+ description: "Verify DNS configuration and provision SSL for a custom domain.",
219
+ inputSchema: {
220
+ type: "object",
221
+ properties: {
222
+ directory: {
223
+ type: "string",
224
+ description: "Directory with funforge.json (defaults to cwd)",
225
+ },
226
+ domain: {
227
+ type: "string",
228
+ description: "Domain name or domain ID to verify",
229
+ },
230
+ },
231
+ required: ["domain"],
232
+ },
233
+ },
234
+ {
235
+ name: "funforge_stop",
236
+ description: "Stop the current deployment for the linked app. The app will be scaled down to 0 replicas.",
237
+ inputSchema: {
238
+ type: "object",
239
+ properties: {
240
+ directory: {
241
+ type: "string",
242
+ description: "Directory with funforge.json (defaults to cwd)",
243
+ },
244
+ },
245
+ required: [],
246
+ },
247
+ },
248
+ {
249
+ name: "funforge_start",
250
+ description: "Start a stopped deployment for the linked app. Scales the app back up.",
251
+ inputSchema: {
252
+ type: "object",
253
+ properties: {
254
+ directory: {
255
+ type: "string",
256
+ description: "Directory with funforge.json (defaults to cwd)",
257
+ },
258
+ },
259
+ required: [],
260
+ },
261
+ },
262
+ {
263
+ name: "funforge_cancel",
264
+ description: "Cancel an in-progress deployment for the linked app. Use this to abort a build or deployment that is stuck.",
265
+ inputSchema: {
266
+ type: "object",
267
+ properties: {
268
+ directory: {
269
+ type: "string",
270
+ description: "Directory with funforge.json (defaults to cwd)",
271
+ },
272
+ },
273
+ required: [],
274
+ },
275
+ },
276
+ {
277
+ name: "funforge_apps_delete",
278
+ description: "Delete an app permanently. This will stop all deployments and remove all associated resources.",
279
+ inputSchema: {
280
+ type: "object",
281
+ properties: {
282
+ appId: {
283
+ type: "string",
284
+ description: "The app ID to delete",
285
+ },
286
+ },
287
+ required: ["appId"],
288
+ },
289
+ },
198
290
  ];
199
291
  async function handleToolCall(name, args) {
200
292
  try {
@@ -219,6 +311,18 @@ async function handleToolCall(name, args) {
219
311
  return await handleDomainsList(args.directory);
220
312
  case "funforge_domains_add":
221
313
  return await handleDomainsAdd(args.directory, args.domain, args.verificationMethod);
314
+ case "funforge_domains_remove":
315
+ return await handleDomainsRemove(args.directory, args.domain);
316
+ case "funforge_domains_verify":
317
+ return await handleDomainsVerify(args.directory, args.domain);
318
+ case "funforge_stop":
319
+ return await handleStop(args.directory);
320
+ case "funforge_start":
321
+ return await handleStart(args.directory);
322
+ case "funforge_cancel":
323
+ return await handleCancel(args.directory);
324
+ case "funforge_apps_delete":
325
+ return await handleAppsDelete(args.appId);
222
326
  default:
223
327
  return errorResult(`Unknown tool: ${name}`);
224
328
  }
@@ -427,6 +531,126 @@ async function handleDomainsAdd(directory, domain, verificationMethod) {
427
531
  nextStep: "Add the DNS record above, then verify with funforge domains verify command",
428
532
  });
429
533
  }
534
+ async function handleDomainsRemove(directory, domain) {
535
+ requireAuth();
536
+ const dir = directory || process.cwd();
537
+ const appId = await getLinkedAppId(dir);
538
+ if (!appId) {
539
+ return errorResult(`No app linked in ${dir}.`);
540
+ }
541
+ const { domains } = await apiRequest(`/api/cli/apps/${appId}/domains`);
542
+ const cleanDomain = domain.toLowerCase();
543
+ const targetDomain = domains.find((d) => d.domain.toLowerCase() === cleanDomain || d.id === domain);
544
+ if (!targetDomain) {
545
+ return errorResult(`Domain not found: ${domain}. Use funforge_domains_list to see available domains.`);
546
+ }
547
+ await apiRequest(`/api/cli/apps/${appId}/domains/${targetDomain.id}`, {
548
+ method: "DELETE",
549
+ });
550
+ return successResult({
551
+ message: `Domain ${targetDomain.domain} removed`,
552
+ domainId: targetDomain.id,
553
+ });
554
+ }
555
+ async function handleDomainsVerify(directory, domain) {
556
+ requireAuth();
557
+ const dir = directory || process.cwd();
558
+ const appId = await getLinkedAppId(dir);
559
+ if (!appId) {
560
+ return errorResult(`No app linked in ${dir}.`);
561
+ }
562
+ const { domains } = await apiRequest(`/api/cli/apps/${appId}/domains`);
563
+ const cleanDomain = domain.toLowerCase();
564
+ const targetDomain = domains.find((d) => d.domain.toLowerCase() === cleanDomain || d.id === domain);
565
+ if (!targetDomain) {
566
+ return errorResult(`Domain not found: ${domain}. Use funforge_domains_list to see available domains.`);
567
+ }
568
+ if (targetDomain.verified) {
569
+ return successResult({
570
+ message: `Domain ${targetDomain.domain} is already verified`,
571
+ domain: targetDomain.domain,
572
+ verified: true,
573
+ });
574
+ }
575
+ const result = await apiRequest(`/api/cli/apps/${appId}/domains/${targetDomain.id}/verify`, { method: "POST" });
576
+ if (result.verified) {
577
+ return successResult({
578
+ message: `Domain ${targetDomain.domain} verified successfully`,
579
+ domain: targetDomain.domain,
580
+ verified: true,
581
+ sslStatus: result.sslStatus,
582
+ });
583
+ }
584
+ return successResult({
585
+ message: result.message || "Domain verification failed",
586
+ domain: targetDomain.domain,
587
+ verified: false,
588
+ hint: "Make sure your DNS record is correctly configured. DNS changes can take up to 48 hours to propagate.",
589
+ });
590
+ }
591
+ async function handleStop(directory) {
592
+ requireAuth();
593
+ const dir = directory || process.cwd();
594
+ const appId = await getLinkedAppId(dir);
595
+ if (!appId) {
596
+ return errorResult(`No app linked in ${dir}.`);
597
+ }
598
+ await apiRequest(`/api/cli/apps/${appId}/stop`, {
599
+ method: "POST",
600
+ });
601
+ return successResult({
602
+ message: "Deployment stopped",
603
+ appId,
604
+ note: "Use funforge_start to restart the deployment",
605
+ });
606
+ }
607
+ async function handleStart(directory) {
608
+ requireAuth();
609
+ const dir = directory || process.cwd();
610
+ const appId = await getLinkedAppId(dir);
611
+ if (!appId) {
612
+ return errorResult(`No app linked in ${dir}.`);
613
+ }
614
+ await apiRequest(`/api/cli/apps/${appId}/start`, {
615
+ method: "POST",
616
+ });
617
+ return successResult({
618
+ message: "Deployment started",
619
+ appId,
620
+ });
621
+ }
622
+ async function handleCancel(directory) {
623
+ requireAuth();
624
+ const dir = directory || process.cwd();
625
+ const appId = await getLinkedAppId(dir);
626
+ if (!appId) {
627
+ return errorResult(`No app linked in ${dir}.`);
628
+ }
629
+ await apiRequest(`/api/cli/apps/${appId}/cancel`, {
630
+ method: "POST",
631
+ });
632
+ return successResult({
633
+ message: "Deployment canceled",
634
+ appId,
635
+ note: "The in-progress build or deployment has been canceled",
636
+ });
637
+ }
638
+ async function handleAppsDelete(appId) {
639
+ requireAuth();
640
+ const { app } = await apiRequest(`/api/cli/apps/${appId}`);
641
+ await apiRequest(`/api/cli/apps/${appId}`, {
642
+ method: "DELETE",
643
+ });
644
+ return successResult({
645
+ message: `App "${app.name}" deleted successfully`,
646
+ app: {
647
+ id: app.id,
648
+ name: app.name,
649
+ slug: app.slug,
650
+ },
651
+ note: "All deployments and resources have been removed",
652
+ });
653
+ }
430
654
  // ============================================
431
655
  // HELPERS
432
656
  // ============================================
@@ -470,7 +694,7 @@ function formatError(error) {
470
694
  async function main() {
471
695
  const server = new Server({
472
696
  name: "funforge",
473
- version: "0.1.0",
697
+ version: "0.3.0",
474
698
  }, {
475
699
  capabilities: {
476
700
  tools: {},
@@ -10,8 +10,14 @@ export interface FunForgeConfig {
10
10
  appName?: string;
11
11
  /** App slug (subdomain) */
12
12
  appSlug?: string;
13
+ /** Root directory for monorepo builds (e.g., "apps/web") */
14
+ rootDirectory?: string;
13
15
  /** Build settings */
14
16
  build?: {
17
+ /** Build mode: 'auto' (default) | 'dockerfile' | 'railpack' */
18
+ mode?: "auto" | "dockerfile" | "railpack";
19
+ /** Path to Dockerfile (relative to root directory, default: 'Dockerfile') */
20
+ dockerfilePath?: string;
15
21
  /** Custom build command */
16
22
  buildCommand?: string;
17
23
  /** Custom install command */
@@ -1 +1 @@
1
- {"version":3,"file":"project-config.d.ts","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,KAAK,CAAC,EAAE;QACN,2BAA2B;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,6BAA6B;QAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,2BAA2B;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,sBAAsB;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,GAAE,MAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAOtE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAOhC;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,EAChC,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,cAAc,CAAC,CAKzB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB"}
1
+ {"version":3,"file":"project-config.d.ts","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB;IACrB,KAAK,CAAC,EAAE;QACN,+DAA+D;QAC/D,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,CAAC;QAC1C,6EAA6E;QAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,2BAA2B;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,6BAA6B;QAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,2BAA2B;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,sBAAsB;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,GAAE,MAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAOtE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAOhC;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,EAChC,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,cAAc,CAAC,CAKzB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byfungsi/funforge",
3
- "version": "0.2.3",
3
+ "version": "0.5.0",
4
4
  "description": "Deploy without git, without leaving your editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,10 +9,7 @@
9
9
  "funforge": "./dist/cli.js",
10
10
  "funforge-mcp": "./dist/mcp.js"
11
11
  },
12
- "files": [
13
- "dist",
14
- "README.md"
15
- ],
12
+ "files": ["dist", "README.md"],
16
13
  "scripts": {
17
14
  "build": "tsc",
18
15
  "dev": "tsc --watch",
@@ -26,13 +23,7 @@
26
23
  "release:publish": "pnpm run build && pnpm run test && npm publish --access public",
27
24
  "release:dry": "pnpm run build && pnpm run test && npm publish --access public --dry-run"
28
25
  },
29
- "keywords": [
30
- "cli",
31
- "deploy",
32
- "funforge",
33
- "fungsi",
34
- "mcp"
35
- ],
26
+ "keywords": ["cli", "deploy", "funforge", "fungsi", "mcp"],
36
27
  "author": "Fungsi",
37
28
  "license": "MIT",
38
29
  "engines": {