@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.
- package/dist/__tests__/api.test.js +13 -3
- package/dist/api.d.ts +1 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/commands/apps.d.ts +3 -2
- package/dist/commands/apps.d.ts.map +1 -1
- package/dist/commands/apps.js +22 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -0
- package/dist/mcp.js +21 -4
- package/dist/tiers.d.ts +38 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +168 -0
- package/package.json +2 -1
|
@@ -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({
|
|
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({
|
|
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({
|
|
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
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]")
|
package/dist/commands/apps.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/commands/apps.js
CHANGED
|
@@ -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
|
-
|
|
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:"));
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
}
|
package/dist/tiers.d.ts
ADDED
|
@@ -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.
|
|
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",
|