@byfungsi/funforge 0.2.0 → 0.2.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.
@@ -18,7 +18,7 @@ describe("config", () => {
18
18
  expect(config.apiUrl).toBe("https://api.fungsi.app");
19
19
  });
20
20
  it("should have production auth URL", () => {
21
- expect(config.authUrl).toBe("https://cloud.fungsi.app");
21
+ expect(config.authUrl).toBe("https://funforge.fungsi.app");
22
22
  });
23
23
  it("should have 30s timeout", () => {
24
24
  expect(config.timeout).toBe(30000);
@@ -31,7 +31,7 @@ describe("config", () => {
31
31
  delete process.env.FUNFORGE_TIMEOUT;
32
32
  const result = getConfig();
33
33
  expect(result.apiUrl).toBe("https://api.fungsi.app");
34
- expect(result.authUrl).toBe("https://cloud.fungsi.app");
34
+ expect(result.authUrl).toBe("https://funforge.fungsi.app");
35
35
  expect(result.timeout).toBe(30000);
36
36
  });
37
37
  it("should override apiUrl from environment", () => {
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
@@ -78,14 +78,28 @@ export async function initDeviceAuth() {
78
78
  */
79
79
  export async function pollDeviceAuth(deviceCode) {
80
80
  const config = getConfig();
81
- const response = await fetch(`${config.apiUrl}/api/cli/auth/device/poll`, {
82
- method: "POST",
83
- headers: { "Content-Type": "application/json" },
84
- body: JSON.stringify({ device_code: deviceCode }),
85
- });
81
+ let response;
82
+ try {
83
+ response = await fetch(`${config.apiUrl}/api/cli/auth/device/poll`, {
84
+ method: "POST",
85
+ headers: { "Content-Type": "application/json" },
86
+ body: JSON.stringify({ device_code: deviceCode }),
87
+ });
88
+ }
89
+ catch (fetchError) {
90
+ // Network error - throw with details
91
+ const err = fetchError;
92
+ throw new ApiError(`Network error: ${err.message}`, 0);
93
+ }
86
94
  // Handle non-200 responses that contain error info
87
95
  if (!response.ok) {
88
- const data = (await response.json());
96
+ let data = {};
97
+ try {
98
+ data = (await response.json());
99
+ }
100
+ catch {
101
+ // couldn't parse JSON
102
+ }
89
103
  // Map OAuth error codes to our status
90
104
  if (data.error === "access_denied") {
91
105
  return { status: "denied" };
@@ -93,7 +107,7 @@ export async function pollDeviceAuth(deviceCode) {
93
107
  if (data.error === "expired_token") {
94
108
  return { status: "expired" };
95
109
  }
96
- throw new ApiError("Failed to poll auth status", response.status);
110
+ throw new ApiError(`Failed to poll auth status: ${data.error_description || data.error || "unknown"}`, response.status, data);
97
111
  }
98
112
  // API returns snake_case, map to camelCase
99
113
  const data = (await response.json());
package/dist/cli.js CHANGED
@@ -4,8 +4,12 @@
4
4
  *
5
5
  * Deploy without git, without leaving your editor.
6
6
  */
7
+ import { createRequire } from "node:module";
7
8
  import chalk from "chalk";
8
9
  import { Command } from "commander";
10
+ // Load version from package.json
11
+ const require = createRequire(import.meta.url);
12
+ const pkg = require("../package.json");
9
13
  import { appsCreateCommand, appsListCommand, linkCommand, } from "./commands/apps.js";
10
14
  // Import commands
11
15
  import { loginCommand, logoutCommand, whoamiCommand } from "./commands/auth.js";
@@ -18,7 +22,7 @@ const program = new Command();
18
22
  program
19
23
  .name("funforge")
20
24
  .description("Deploy without git, without leaving your editor")
21
- .version("0.1.0");
25
+ .version(pkg.version);
22
26
  // ============================================
23
27
  // AUTH COMMANDS
24
28
  // ============================================
@@ -1 +1 @@
1
- {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH;;;;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,CAAA;CAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CAoBf;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;AASH;;;;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,CAAA;CAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8D/D"}
@@ -7,8 +7,9 @@
7
7
  */
8
8
  import chalk from "chalk";
9
9
  import ora from "ora";
10
- import { ApiError, createApp, getApp, listApps } from "../api.js";
10
+ import { createApp, getApp, listApps } from "../api.js";
11
11
  import { isAuthenticated } from "../credentials.js";
12
+ import { handleError } from "../errors.js";
12
13
  import { readConfig, updateConfig } from "../project-config.js";
13
14
  /**
14
15
  * funforge apps list
@@ -132,20 +133,3 @@ function requireAuth() {
132
133
  process.exit(1);
133
134
  }
134
135
  }
135
- /**
136
- * Handle API errors
137
- */
138
- function handleError(error) {
139
- if (error instanceof ApiError) {
140
- console.error(chalk.red(`Error ${error.statusCode}: ${error.message}`));
141
- if (error.body &&
142
- typeof error.body === "object" &&
143
- "message" in error.body) {
144
- console.error(chalk.gray(error.body.message));
145
- }
146
- }
147
- else {
148
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
149
- }
150
- process.exit(1);
151
- }
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH;;;;;GAKG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA+FlD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CASnD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAcnD"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH;;;;;GAKG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAsGlD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CASnD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAcnD"}
@@ -54,7 +54,16 @@ export async function loginCommand() {
54
54
  const expiresAt = startTime + auth.expiresIn * 1000;
55
55
  while (Date.now() < expiresAt) {
56
56
  await sleep(auth.interval * 1000);
57
- const result = await pollDeviceAuth(auth.deviceCode);
57
+ let result;
58
+ try {
59
+ result = await pollDeviceAuth(auth.deviceCode);
60
+ }
61
+ catch (pollError) {
62
+ const err = pollError;
63
+ pollSpinner.fail("Poll failed");
64
+ console.error(chalk.red(err.message));
65
+ process.exit(1);
66
+ }
58
67
  if (result.status === "authorized") {
59
68
  pollSpinner.succeed("Authenticated!");
60
69
  // Save credentials
@@ -85,7 +94,8 @@ export async function loginCommand() {
85
94
  }
86
95
  catch (error) {
87
96
  spinner.fail("Authentication failed");
88
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
97
+ const err = error;
98
+ console.error(chalk.red(err.message));
89
99
  process.exit(1);
90
100
  }
91
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAwEH;;;;;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;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"}
@@ -10,8 +10,9 @@
10
10
  */
11
11
  import chalk from "chalk";
12
12
  import ora from "ora";
13
- import { ApiError, getAppDetails, updateApp, } from "../api.js";
13
+ import { getAppDetails, updateApp } from "../api.js";
14
14
  import { isAuthenticated } from "../credentials.js";
15
+ import { handleError } from "../errors.js";
15
16
  import { readConfig, updateConfig, } from "../project-config.js";
16
17
  /**
17
18
  * Extract build settings from funforge.json config
@@ -268,20 +269,3 @@ function requireAuth() {
268
269
  process.exit(1);
269
270
  }
270
271
  }
271
- /**
272
- * Handle API errors
273
- */
274
- function handleError(error) {
275
- if (error instanceof ApiError) {
276
- console.error(chalk.red(`Error ${error.statusCode}: ${error.message}`));
277
- if (error.body &&
278
- typeof error.body === "object" &&
279
- "message" in error.body) {
280
- console.error(chalk.gray(error.body.message));
281
- }
282
- }
283
- else {
284
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
285
- }
286
- process.exit(1);
287
- }
@@ -10,8 +10,9 @@
10
10
  */
11
11
  import chalk from "chalk";
12
12
  import ora from "ora";
13
- import { ApiError, createDeployment, getDeployment, getUploadUrl, uploadTarball, } from "../api.js";
13
+ import { createDeployment, getDeployment, getUploadUrl, uploadTarball, } from "../api.js";
14
14
  import { isAuthenticated } from "../credentials.js";
15
+ import { handleError } from "../errors.js";
15
16
  import { readConfig } from "../project-config.js";
16
17
  import { createTarball, formatSize, readTarball } from "../tarball.js";
17
18
  /**
@@ -174,23 +175,6 @@ function requireAuth() {
174
175
  process.exit(1);
175
176
  }
176
177
  }
177
- /**
178
- * Handle API errors
179
- */
180
- function handleError(error) {
181
- if (error instanceof ApiError) {
182
- console.error(chalk.red(`Error ${error.statusCode}: ${error.message}`));
183
- if (error.body &&
184
- typeof error.body === "object" &&
185
- "message" in error.body) {
186
- console.error(chalk.gray(error.body.message));
187
- }
188
- }
189
- else {
190
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
191
- }
192
- process.exit(1);
193
- }
194
178
  function sleep(ms) {
195
179
  return new Promise((resolve) => setTimeout(resolve, ms));
196
180
  }
@@ -1 +1 @@
1
- {"version":3,"file":"domains.d.ts","sourceRoot":"","sources":["../../src/commands/domains.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAqCH;;;;GAIG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CA6CxD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCxE;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DxE"}
1
+ {"version":3,"file":"domains.d.ts","sourceRoot":"","sources":["../../src/commands/domains.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAsCH;;;;GAIG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CA6CxD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCxE;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DxE"}
@@ -8,8 +8,9 @@
8
8
  */
9
9
  import chalk from "chalk";
10
10
  import ora from "ora";
11
- import { ApiError, apiRequest } from "../api.js";
11
+ import { apiRequest } from "../api.js";
12
12
  import { isAuthenticated } from "../credentials.js";
13
+ import { handleError } from "../errors.js";
13
14
  import { readConfig } from "../project-config.js";
14
15
  /**
15
16
  * funforge domains list
@@ -198,20 +199,3 @@ function requireAuth() {
198
199
  process.exit(1);
199
200
  }
200
201
  }
201
- /**
202
- * Handle API errors
203
- */
204
- function handleError(error) {
205
- if (error instanceof ApiError) {
206
- console.error(chalk.red(`Error ${error.statusCode}: ${error.message}`));
207
- if (error.body &&
208
- typeof error.body === "object" &&
209
- "message" in error.body) {
210
- console.error(chalk.gray(error.body.message));
211
- }
212
- }
213
- else {
214
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
215
- }
216
- process.exit(1);
217
- }
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAqDpD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDlE;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCnE"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAqDpD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDlE;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCnE"}
@@ -7,8 +7,9 @@
7
7
  */
8
8
  import chalk from "chalk";
9
9
  import ora from "ora";
10
- import { ApiError, apiRequest } from "../api.js";
10
+ import { apiRequest } from "../api.js";
11
11
  import { isAuthenticated } from "../credentials.js";
12
+ import { handleError } from "../errors.js";
12
13
  import { readConfig } from "../project-config.js";
13
14
  /**
14
15
  * funforge env list
@@ -164,20 +165,3 @@ function requireAuth() {
164
165
  process.exit(1);
165
166
  }
166
167
  }
167
- /**
168
- * Handle API errors
169
- */
170
- function handleError(error) {
171
- if (error instanceof ApiError) {
172
- console.error(chalk.red(`Error ${error.statusCode}: ${error.message}`));
173
- if (error.body &&
174
- typeof error.body === "object" &&
175
- "message" in error.body) {
176
- console.error(chalk.gray(error.body.message));
177
- }
178
- }
179
- else {
180
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
181
- }
182
- process.exit(1);
183
- }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Error Handling Utilities
3
+ *
4
+ * Shared error formatting for user-friendly CLI output.
5
+ */
6
+ import { ApiError } from "./api.js";
7
+ /**
8
+ * Format an API error for user-friendly display.
9
+ *
10
+ * Handles known error codes with actionable hints:
11
+ * - 402: Billing/credits issues
12
+ * - 403: Plan limits
13
+ * - 409: Conflicts (deployment in progress)
14
+ * - 404: Not found
15
+ */
16
+ export declare function formatApiError(error: ApiError): string;
17
+ /**
18
+ * Handle any error and exit.
19
+ *
20
+ * Use this in catch blocks for consistent error output.
21
+ */
22
+ export declare function handleError(error: unknown): never;
23
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAWpC;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CA6DtD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CASjD"}
package/dist/errors.js ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Error Handling Utilities
3
+ *
4
+ * Shared error formatting for user-friendly CLI output.
5
+ */
6
+ import chalk from "chalk";
7
+ import { ApiError } from "./api.js";
8
+ /**
9
+ * Format an API error for user-friendly display.
10
+ *
11
+ * Handles known error codes with actionable hints:
12
+ * - 402: Billing/credits issues
13
+ * - 403: Plan limits
14
+ * - 409: Conflicts (deployment in progress)
15
+ * - 404: Not found
16
+ */
17
+ export function formatApiError(error) {
18
+ const body = error.body;
19
+ const code = body?.error?.code;
20
+ const message = body?.error?.message || body?.message || error.message;
21
+ // 402 - Payment Required (billing/credits)
22
+ if (error.statusCode === 402 || code === "BILLING_ERROR") {
23
+ return [
24
+ chalk.red(message),
25
+ "",
26
+ chalk.yellow("Add credits at: https://funforge.fungsi.app/balance"),
27
+ ].join("\n");
28
+ }
29
+ // 403 - Plan limits exceeded
30
+ if (code === "PLAN_LIMIT_EXCEEDED" || code === "APP_LIMIT_EXCEEDED") {
31
+ return [
32
+ chalk.red(message),
33
+ "",
34
+ chalk.yellow("Delete unused apps or upgrade your plan to continue."),
35
+ ].join("\n");
36
+ }
37
+ // 409 - Conflict (deployment in progress, etc.)
38
+ if (error.statusCode === 409 || code === "DEPLOYMENT_IN_PROGRESS") {
39
+ return [
40
+ chalk.red(message),
41
+ "",
42
+ chalk.gray("Wait for the current deployment to complete and try again."),
43
+ ].join("\n");
44
+ }
45
+ // 404 - Not found
46
+ if (error.statusCode === 404) {
47
+ return chalk.red(message || "Resource not found.");
48
+ }
49
+ // 401 - Unauthorized
50
+ if (error.statusCode === 401) {
51
+ return [
52
+ chalk.red(message || "Not authenticated."),
53
+ "",
54
+ chalk.gray("Run `funforge login` to authenticate."),
55
+ ].join("\n");
56
+ }
57
+ // 400 - Bad request / validation
58
+ if (error.statusCode === 400) {
59
+ return chalk.red(message || "Invalid request.");
60
+ }
61
+ // 500+ - Server errors
62
+ if (error.statusCode >= 500) {
63
+ return [
64
+ chalk.red("Server error. Please try again later."),
65
+ chalk.gray(message),
66
+ ].join("\n");
67
+ }
68
+ // Default: show the message
69
+ return chalk.red(message);
70
+ }
71
+ /**
72
+ * Handle any error and exit.
73
+ *
74
+ * Use this in catch blocks for consistent error output.
75
+ */
76
+ export function handleError(error) {
77
+ if (error instanceof ApiError) {
78
+ console.error(formatApiError(error));
79
+ }
80
+ else if (error instanceof Error) {
81
+ console.error(chalk.red(error.message));
82
+ }
83
+ else {
84
+ console.error(chalk.red(String(error)));
85
+ }
86
+ process.exit(1);
87
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byfungsi/funforge",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Deploy without git, without leaving your editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,16 +9,30 @@
9
9
  "funforge": "./dist/cli.js",
10
10
  "funforge-mcp": "./dist/mcp.js"
11
11
  },
12
- "files": ["dist", "README.md"],
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
13
16
  "scripts": {
14
17
  "build": "tsc",
15
18
  "dev": "tsc --watch",
16
19
  "typecheck": "tsc --noEmit",
17
20
  "test": "vitest run",
18
21
  "start": "node dist/cli.js",
19
- "mcp": "node dist/mcp.js"
22
+ "mcp": "node dist/mcp.js",
23
+ "release:patch": "npm version patch --no-git-tag-version && pnpm run release:publish",
24
+ "release:minor": "npm version minor --no-git-tag-version && pnpm run release:publish",
25
+ "release:major": "npm version major --no-git-tag-version && pnpm run release:publish",
26
+ "release:publish": "pnpm run build && pnpm run test && npm publish --access public",
27
+ "release:dry": "pnpm run build && pnpm run test && npm publish --access public --dry-run"
20
28
  },
21
- "keywords": ["cli", "deploy", "funforge", "fungsi", "mcp"],
29
+ "keywords": [
30
+ "cli",
31
+ "deploy",
32
+ "funforge",
33
+ "fungsi",
34
+ "mcp"
35
+ ],
22
36
  "author": "Fungsi",
23
37
  "license": "MIT",
24
38
  "engines": {