@byfungsi/funforge 0.2.2 → 0.2.3

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.
@@ -130,11 +130,17 @@ describe("api", () => {
130
130
  app: { id: "new-app", name: "New App", slug: "new-app" },
131
131
  }),
132
132
  });
133
- const result = await createApp({ name: "New App" });
133
+ const result = await createApp({
134
+ name: "New App",
135
+ tierKey: "cost-optimized.small",
136
+ });
134
137
  expect(result.app.name).toBe("New App");
135
138
  expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining("/api/cli/apps"), expect.objectContaining({
136
139
  method: "POST",
137
- body: JSON.stringify({ name: "New App" }),
140
+ body: JSON.stringify({
141
+ name: "New App",
142
+ tierKey: "cost-optimized.small",
143
+ }),
138
144
  }));
139
145
  });
140
146
  it("should create app with name and slug", async () => {
@@ -144,7 +150,11 @@ describe("api", () => {
144
150
  app: { id: "new-app", name: "New App", slug: "custom-slug" },
145
151
  }),
146
152
  });
147
- const result = await createApp({ name: "New App", slug: "custom-slug" });
153
+ const result = await createApp({
154
+ name: "New App",
155
+ slug: "custom-slug",
156
+ tierKey: "cost-optimized.small",
157
+ });
148
158
  expect(result.app.slug).toBe("custom-slug");
149
159
  });
150
160
  });
package/dist/api.d.ts CHANGED
@@ -105,6 +105,7 @@ export declare function updateApp(appId: string, settings: UpdateAppSettings): P
105
105
  export declare function createApp(data: {
106
106
  name: string;
107
107
  slug?: string;
108
+ tierKey: string;
108
109
  }): Promise<{
109
110
  app: App;
110
111
  }>;
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/cli.js CHANGED
@@ -47,6 +47,7 @@ appsCmd
47
47
  .command("create <name>")
48
48
  .description("Create a new app")
49
49
  .option("-s, --slug <slug>", "Custom slug (subdomain)")
50
+ .option("-t, --tier <tier>", "Tier key (e.g., cost-optimized.small)")
50
51
  .action(appsCreateCommand);
51
52
  program
52
53
  .command("link [appId]")
@@ -2,7 +2,7 @@
2
2
  * Apps Commands
3
3
  *
4
4
  * - apps list: List all apps
5
- * - apps create: Create a new app
5
+ * - apps create: Create a new app (with interactive tier selection)
6
6
  * - link: Link current directory to an app
7
7
  */
8
8
  /**
@@ -14,10 +14,11 @@ export declare function appsListCommand(): Promise<void>;
14
14
  /**
15
15
  * funforge apps create <name>
16
16
  *
17
- * Create a new app.
17
+ * Create a new app with interactive tier selection.
18
18
  */
19
19
  export declare function appsCreateCommand(name: string, options: {
20
20
  slug?: string;
21
+ tier?: string;
21
22
  }): Promise<void>;
22
23
  /**
23
24
  * funforge link [appId]
@@ -1 +1 @@
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"}
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"}
@@ -2,7 +2,7 @@
2
2
  * Apps Commands
3
3
  *
4
4
  * - apps list: List all apps
5
- * - apps create: Create a new app
5
+ * - apps create: Create a new app (with interactive tier selection)
6
6
  * - link: Link current directory to an app
7
7
  */
8
8
  import chalk from "chalk";
@@ -11,6 +11,7 @@ import { createApp, getApp, listApps } from "../api.js";
11
11
  import { isAuthenticated } from "../credentials.js";
12
12
  import { handleError } from "../errors.js";
13
13
  import { readConfig, updateConfig } from "../project-config.js";
14
+ import { VALID_TIER_KEYS, getTierInfo, isValidTierKey, selectTier, } from "../tiers.js";
14
15
  /**
15
16
  * funforge apps list
16
17
  *
@@ -44,17 +45,34 @@ export async function appsListCommand() {
44
45
  /**
45
46
  * funforge apps create <name>
46
47
  *
47
- * Create a new app.
48
+ * Create a new app with interactive tier selection.
48
49
  */
49
50
  export async function appsCreateCommand(name, options) {
50
51
  requireAuth();
51
- const spinner = ora(`Creating app "${name}"...`).start();
52
+ // Get tier - from flag or interactive prompt
53
+ let tierKey;
54
+ if (options.tier) {
55
+ if (!isValidTierKey(options.tier)) {
56
+ console.log(chalk.red(`Invalid tier: ${options.tier}`));
57
+ console.log(chalk.gray(`Valid tiers: ${VALID_TIER_KEYS.join(", ")}`));
58
+ process.exit(1);
59
+ }
60
+ tierKey = options.tier;
61
+ }
62
+ else {
63
+ tierKey = await selectTier();
64
+ }
65
+ const tierInfo = getTierInfo(tierKey);
66
+ const spinner = ora(`Creating app "${name}" with ${tierInfo?.name ?? tierKey} tier...`).start();
52
67
  try {
53
- const { app } = await createApp({ name, slug: options.slug });
68
+ const { app } = await createApp({ name, slug: options.slug, tierKey });
54
69
  spinner.succeed(`App created!`);
55
70
  console.log();
56
71
  console.log(` Name: ${app.name}`);
57
72
  console.log(` Slug: ${chalk.cyan(app.slug)}`);
73
+ if (tierInfo) {
74
+ console.log(` Tier: ${tierInfo.category}/${tierInfo.name} (${tierInfo.credits} credits/mo)`);
75
+ }
58
76
  console.log(` ID: ${app.id}`);
59
77
  console.log();
60
78
  console.log(chalk.gray("Link this directory with:"));
@@ -1 +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"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAmBpC;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CA0EtD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CASjD"}
package/dist/errors.js CHANGED
@@ -56,6 +56,17 @@ export function formatApiError(error) {
56
56
  }
57
57
  // 400 - Bad request / validation
58
58
  if (error.statusCode === 400) {
59
+ const details = body?.error?.details;
60
+ if (details && Array.isArray(details) && details.length > 0) {
61
+ // Format Zod validation issues nicely
62
+ const issues = details
63
+ .map((d) => {
64
+ const field = d.path?.join(".") || "input";
65
+ return ` - ${field}: ${d.message}`;
66
+ })
67
+ .join("\n");
68
+ return [chalk.red(message || "Invalid input:"), chalk.gray(issues)].join("\n");
69
+ }
59
70
  return chalk.red(message || "Invalid request.");
60
71
  }
61
72
  // 500+ - Server errors
package/dist/mcp.js CHANGED
@@ -58,8 +58,24 @@ const TOOLS = [
58
58
  type: "string",
59
59
  description: "Custom slug/subdomain (optional, auto-generated if not provided)",
60
60
  },
61
+ tierKey: {
62
+ type: "string",
63
+ description: "Tier key for the app. Cost-optimized tiers auto-sleep after 3h idle. Standard tiers are always-on. Options: cost-optimized.micro (10 credits/mo), cost-optimized.small (25 credits/mo), cost-optimized.medium (50 credits/mo), cost-optimized.large (100 credits/mo), cost-optimized.xlarge (125 credits/mo), standard.micro (50 credits/mo), standard.small (75 credits/mo), standard.medium (125 credits/mo), standard.large (200 credits/mo), standard.xlarge (250 credits/mo)",
64
+ enum: [
65
+ "cost-optimized.micro",
66
+ "cost-optimized.small",
67
+ "cost-optimized.medium",
68
+ "cost-optimized.large",
69
+ "cost-optimized.xlarge",
70
+ "standard.micro",
71
+ "standard.small",
72
+ "standard.medium",
73
+ "standard.large",
74
+ "standard.xlarge",
75
+ ],
76
+ },
61
77
  },
62
- required: ["name"],
78
+ required: ["name", "tierKey"],
63
79
  },
64
80
  },
65
81
  {
@@ -188,7 +204,7 @@ async function handleToolCall(name, args) {
188
204
  case "funforge_apps_list":
189
205
  return await handleAppsList();
190
206
  case "funforge_apps_create":
191
- return await handleAppsCreate(args.name, args.slug);
207
+ return await handleAppsCreate(args.name, args.tierKey, args.slug);
192
208
  case "funforge_status":
193
209
  return await handleStatus(args.directory);
194
210
  case "funforge_deploy":
@@ -240,15 +256,16 @@ async function handleAppsList() {
240
256
  })),
241
257
  });
242
258
  }
243
- async function handleAppsCreate(name, slug) {
259
+ async function handleAppsCreate(name, tierKey, slug) {
244
260
  requireAuth();
245
- const { app } = await createApp({ name, slug });
261
+ const { app } = await createApp({ name, slug, tierKey });
246
262
  return successResult({
247
263
  message: `App "${app.name}" created successfully`,
248
264
  app: {
249
265
  id: app.id,
250
266
  name: app.name,
251
267
  slug: app.slug,
268
+ tierKey,
252
269
  },
253
270
  });
254
271
  }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * FunForge Tier Selection
3
+ *
4
+ * Interactive tier picker for app creation.
5
+ */
6
+ /** Tier configuration for display */
7
+ interface TierOption {
8
+ key: string;
9
+ name: string;
10
+ credits: number;
11
+ priceIdr: string;
12
+ memory: string;
13
+ cpu: string;
14
+ category: "cost-optimized" | "standard";
15
+ }
16
+ /**
17
+ * All available tiers grouped by category
18
+ *
19
+ * Cost-Optimized: Auto-sleeps after 3h idle (cheaper)
20
+ * Standard: Always-on (higher availability)
21
+ */
22
+ export declare const TIER_OPTIONS: TierOption[];
23
+ export declare const VALID_TIER_KEYS: string[];
24
+ export declare const DEFAULT_TIER = "cost-optimized.small";
25
+ /**
26
+ * Interactive tier selection prompt
27
+ */
28
+ export declare function selectTier(): Promise<string>;
29
+ /**
30
+ * Check if a string is a valid tier key
31
+ */
32
+ export declare function isValidTierKey(key: string): boolean;
33
+ /**
34
+ * Get tier display info by key
35
+ */
36
+ export declare function getTierInfo(key: string): TierOption | undefined;
37
+ export {};
38
+ //# sourceMappingURL=tiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiers.d.ts","sourceRoot":"","sources":["../src/tiers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,qCAAqC;AACrC,UAAU,UAAU;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,gBAAgB,GAAG,UAAU,CAAC;CACzC;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,UAAU,EA6FpC,CAAC;AAEF,eAAO,MAAM,eAAe,UAAiC,CAAC;AAC9D,eAAO,MAAM,YAAY,yBAAyB,CAAC;AAanD;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAsClD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAE/D"}
package/dist/tiers.js ADDED
@@ -0,0 +1,168 @@
1
+ /**
2
+ * FunForge Tier Selection
3
+ *
4
+ * Interactive tier picker for app creation.
5
+ */
6
+ import { select } from "@inquirer/prompts";
7
+ import chalk from "chalk";
8
+ /**
9
+ * All available tiers grouped by category
10
+ *
11
+ * Cost-Optimized: Auto-sleeps after 3h idle (cheaper)
12
+ * Standard: Always-on (higher availability)
13
+ */
14
+ export const TIER_OPTIONS = [
15
+ // Cost-Optimized (auto-sleep after 3h idle)
16
+ {
17
+ key: "cost-optimized.micro",
18
+ name: "Micro",
19
+ credits: 10,
20
+ priceIdr: "Rp 10.000",
21
+ memory: "256MB",
22
+ cpu: "0.1",
23
+ category: "cost-optimized",
24
+ },
25
+ {
26
+ key: "cost-optimized.small",
27
+ name: "Small",
28
+ credits: 25,
29
+ priceIdr: "Rp 25.000",
30
+ memory: "512MB",
31
+ cpu: "0.25",
32
+ category: "cost-optimized",
33
+ },
34
+ {
35
+ key: "cost-optimized.medium",
36
+ name: "Medium",
37
+ credits: 50,
38
+ priceIdr: "Rp 50.000",
39
+ memory: "1GB",
40
+ cpu: "0.5",
41
+ category: "cost-optimized",
42
+ },
43
+ {
44
+ key: "cost-optimized.large",
45
+ name: "Large",
46
+ credits: 100,
47
+ priceIdr: "Rp 100.000",
48
+ memory: "2GB",
49
+ cpu: "1.0",
50
+ category: "cost-optimized",
51
+ },
52
+ {
53
+ key: "cost-optimized.xlarge",
54
+ name: "X-Large",
55
+ credits: 125,
56
+ priceIdr: "Rp 125.000",
57
+ memory: "4GB",
58
+ cpu: "2.0",
59
+ category: "cost-optimized",
60
+ },
61
+ // Standard (always-on)
62
+ {
63
+ key: "standard.micro",
64
+ name: "Micro",
65
+ credits: 50,
66
+ priceIdr: "Rp 50.000",
67
+ memory: "256MB",
68
+ cpu: "0.1",
69
+ category: "standard",
70
+ },
71
+ {
72
+ key: "standard.small",
73
+ name: "Small",
74
+ credits: 75,
75
+ priceIdr: "Rp 75.000",
76
+ memory: "512MB",
77
+ cpu: "0.25",
78
+ category: "standard",
79
+ },
80
+ {
81
+ key: "standard.medium",
82
+ name: "Medium",
83
+ credits: 125,
84
+ priceIdr: "Rp 125.000",
85
+ memory: "1GB",
86
+ cpu: "0.5",
87
+ category: "standard",
88
+ },
89
+ {
90
+ key: "standard.large",
91
+ name: "Large",
92
+ credits: 200,
93
+ priceIdr: "Rp 200.000",
94
+ memory: "2GB",
95
+ cpu: "1.0",
96
+ category: "standard",
97
+ },
98
+ {
99
+ key: "standard.xlarge",
100
+ name: "X-Large",
101
+ credits: 250,
102
+ priceIdr: "Rp 250.000",
103
+ memory: "4GB",
104
+ cpu: "2.0",
105
+ category: "standard",
106
+ },
107
+ ];
108
+ export const VALID_TIER_KEYS = TIER_OPTIONS.map((t) => t.key);
109
+ export const DEFAULT_TIER = "cost-optimized.small";
110
+ /**
111
+ * Format tier for display in dropdown
112
+ */
113
+ function formatTier(t) {
114
+ const nameCol = t.name.padEnd(8);
115
+ const creditsCol = `${t.credits.toString().padStart(3)} credits`;
116
+ const priceCol = `(${t.priceIdr})`;
117
+ const resourcesCol = `${t.memory}, ${t.cpu} CPU`;
118
+ return `${nameCol} ${creditsCol} ${priceCol.padEnd(14)} - ${resourcesCol}`;
119
+ }
120
+ /**
121
+ * Interactive tier selection prompt
122
+ */
123
+ export async function selectTier() {
124
+ console.log();
125
+ console.log(chalk.bold("Select a tier for your app:"));
126
+ console.log();
127
+ const costOptimized = TIER_OPTIONS.filter((t) => t.category === "cost-optimized");
128
+ const standard = TIER_OPTIONS.filter((t) => t.category === "standard");
129
+ const tierKey = await select({
130
+ message: "Tier",
131
+ choices: [
132
+ // Cost-Optimized group header
133
+ {
134
+ value: "_header_cost",
135
+ name: chalk.dim("── Cost-Optimized (auto-sleep after 3h idle) ──"),
136
+ disabled: true,
137
+ },
138
+ ...costOptimized.map((t) => ({
139
+ value: t.key,
140
+ name: ` ${formatTier(t)}`,
141
+ })),
142
+ // Standard group header
143
+ {
144
+ value: "_header_standard",
145
+ name: chalk.dim("── Standard (always-on) ──"),
146
+ disabled: true,
147
+ },
148
+ ...standard.map((t) => ({
149
+ value: t.key,
150
+ name: ` ${formatTier(t)}`,
151
+ })),
152
+ ],
153
+ default: DEFAULT_TIER,
154
+ });
155
+ return tierKey;
156
+ }
157
+ /**
158
+ * Check if a string is a valid tier key
159
+ */
160
+ export function isValidTierKey(key) {
161
+ return VALID_TIER_KEYS.includes(key);
162
+ }
163
+ /**
164
+ * Get tier display info by key
165
+ */
166
+ export function getTierInfo(key) {
167
+ return TIER_OPTIONS.find((t) => t.key === key);
168
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byfungsi/funforge",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Deploy without git, without leaving your editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,6 +39,7 @@
39
39
  "node": ">=18.0.0"
40
40
  },
41
41
  "dependencies": {
42
+ "@inquirer/prompts": "^8.2.0",
42
43
  "@modelcontextprotocol/sdk": "^1.12.1",
43
44
  "chalk": "^5.4.1",
44
45
  "commander": "^13.1.0",