@byfungsi/funforge 0.1.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.
- package/README.md +273 -0
- package/dist/__tests__/api.test.d.ts +5 -0
- package/dist/__tests__/api.test.d.ts.map +1 -0
- package/dist/__tests__/api.test.js +177 -0
- package/dist/__tests__/config.test.d.ts +5 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +58 -0
- package/dist/__tests__/mcp.test.d.ts +7 -0
- package/dist/__tests__/mcp.test.d.ts.map +1 -0
- package/dist/__tests__/mcp.test.js +142 -0
- package/dist/__tests__/project-config.test.d.ts +5 -0
- package/dist/__tests__/project-config.test.d.ts.map +1 -0
- package/dist/__tests__/project-config.test.js +122 -0
- package/dist/__tests__/tarball.test.d.ts +5 -0
- package/dist/__tests__/tarball.test.d.ts.map +1 -0
- package/dist/__tests__/tarball.test.js +113 -0
- package/dist/api.d.ts +157 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +165 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +129 -0
- package/dist/commands/apps.d.ts +29 -0
- package/dist/commands/apps.d.ts.map +1 -0
- package/dist/commands/apps.js +151 -0
- package/dist/commands/auth.d.ts +27 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +127 -0
- package/dist/commands/config.d.ts +31 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +287 -0
- package/dist/commands/deploy.d.ts +24 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +196 -0
- package/dist/commands/domains.d.ts +35 -0
- package/dist/commands/domains.d.ts.map +1 -0
- package/dist/commands/domains.js +217 -0
- package/dist/commands/env.d.ts +26 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +183 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/credentials.d.ts +46 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +60 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/mcp.d.ts +19 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +480 -0
- package/dist/project-config.d.ts +47 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +55 -0
- package/dist/tarball.d.ts +29 -0
- package/dist/tarball.d.ts.map +1 -0
- package/dist/tarball.js +148 -0
- package/package.json +45 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* FunForge CLI
|
|
4
|
+
*
|
|
5
|
+
* Deploy without git, without leaving your editor.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import { appsCreateCommand, appsListCommand, linkCommand, } from "./commands/apps.js";
|
|
10
|
+
// Import commands
|
|
11
|
+
import { loginCommand, logoutCommand, whoamiCommand } from "./commands/auth.js";
|
|
12
|
+
import { configPullCommand, configPushCommand, configShowCommand, } from "./commands/config.js";
|
|
13
|
+
import { deployCommand } from "./commands/deploy.js";
|
|
14
|
+
import { domainsAddCommand, domainsListCommand, domainsRemoveCommand, domainsVerifyCommand, } from "./commands/domains.js";
|
|
15
|
+
import { envListCommand, envSetCommand, envUnsetCommand, } from "./commands/env.js";
|
|
16
|
+
const program = new Command();
|
|
17
|
+
// CLI metadata
|
|
18
|
+
program
|
|
19
|
+
.name("funforge")
|
|
20
|
+
.description("Deploy without git, without leaving your editor")
|
|
21
|
+
.version("0.1.0");
|
|
22
|
+
// ============================================
|
|
23
|
+
// AUTH COMMANDS
|
|
24
|
+
// ============================================
|
|
25
|
+
program
|
|
26
|
+
.command("login")
|
|
27
|
+
.description("Authenticate with FunForge")
|
|
28
|
+
.action(loginCommand);
|
|
29
|
+
program
|
|
30
|
+
.command("logout")
|
|
31
|
+
.description("Clear stored credentials")
|
|
32
|
+
.action(logoutCommand);
|
|
33
|
+
program
|
|
34
|
+
.command("whoami")
|
|
35
|
+
.description("Show current authenticated user")
|
|
36
|
+
.action(whoamiCommand);
|
|
37
|
+
// ============================================
|
|
38
|
+
// APP COMMANDS
|
|
39
|
+
// ============================================
|
|
40
|
+
const appsCmd = program.command("apps").description("Manage apps");
|
|
41
|
+
appsCmd.command("list").description("List all apps").action(appsListCommand);
|
|
42
|
+
appsCmd
|
|
43
|
+
.command("create <name>")
|
|
44
|
+
.description("Create a new app")
|
|
45
|
+
.option("-s, --slug <slug>", "Custom slug (subdomain)")
|
|
46
|
+
.action(appsCreateCommand);
|
|
47
|
+
program
|
|
48
|
+
.command("link [appId]")
|
|
49
|
+
.description("Link current directory to an app")
|
|
50
|
+
.action(linkCommand);
|
|
51
|
+
// ============================================
|
|
52
|
+
// DEPLOY COMMAND
|
|
53
|
+
// ============================================
|
|
54
|
+
program
|
|
55
|
+
.command("deploy")
|
|
56
|
+
.description("Deploy the current directory")
|
|
57
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
58
|
+
.option("--no-watch", "Don't watch deployment logs")
|
|
59
|
+
.action(deployCommand);
|
|
60
|
+
// ============================================
|
|
61
|
+
// ENVIRONMENT VARIABLES
|
|
62
|
+
// ============================================
|
|
63
|
+
const envCmd = program
|
|
64
|
+
.command("env")
|
|
65
|
+
.description("Manage environment variables");
|
|
66
|
+
envCmd
|
|
67
|
+
.command("list")
|
|
68
|
+
.description("List environment variables")
|
|
69
|
+
.action(envListCommand);
|
|
70
|
+
envCmd
|
|
71
|
+
.command("set <pairs...>")
|
|
72
|
+
.description("Set environment variables (KEY=VALUE)")
|
|
73
|
+
.action(envSetCommand);
|
|
74
|
+
envCmd
|
|
75
|
+
.command("unset <keys...>")
|
|
76
|
+
.description("Remove environment variables")
|
|
77
|
+
.action(envUnsetCommand);
|
|
78
|
+
// ============================================
|
|
79
|
+
// DOMAIN MANAGEMENT
|
|
80
|
+
// ============================================
|
|
81
|
+
const domainsCmd = program
|
|
82
|
+
.command("domains")
|
|
83
|
+
.description("Manage custom domains");
|
|
84
|
+
domainsCmd
|
|
85
|
+
.command("list")
|
|
86
|
+
.description("List custom domains")
|
|
87
|
+
.action(domainsListCommand);
|
|
88
|
+
domainsCmd
|
|
89
|
+
.command("add <domain>")
|
|
90
|
+
.description("Add a custom domain")
|
|
91
|
+
.option("-m, --method <method>", "Verification method (cname or txt)", "cname")
|
|
92
|
+
.action(domainsAddCommand);
|
|
93
|
+
domainsCmd
|
|
94
|
+
.command("remove <domain>")
|
|
95
|
+
.description("Remove a custom domain")
|
|
96
|
+
.action(domainsRemoveCommand);
|
|
97
|
+
domainsCmd
|
|
98
|
+
.command("verify <domain>")
|
|
99
|
+
.description("Verify DNS and provision SSL")
|
|
100
|
+
.action(domainsVerifyCommand);
|
|
101
|
+
// ============================================
|
|
102
|
+
// CONFIG COMMANDS
|
|
103
|
+
// ============================================
|
|
104
|
+
const configCmd = program
|
|
105
|
+
.command("config")
|
|
106
|
+
.description("Manage build settings (sync funforge.json with server)");
|
|
107
|
+
configCmd
|
|
108
|
+
.command("push")
|
|
109
|
+
.description("Push local funforge.json settings to server")
|
|
110
|
+
.action(configPushCommand);
|
|
111
|
+
configCmd
|
|
112
|
+
.command("pull")
|
|
113
|
+
.description("Pull server settings to local funforge.json")
|
|
114
|
+
.action(configPullCommand);
|
|
115
|
+
configCmd
|
|
116
|
+
.command("show")
|
|
117
|
+
.description("Show comparison between local and server settings")
|
|
118
|
+
.action(configShowCommand);
|
|
119
|
+
// ============================================
|
|
120
|
+
// ERROR HANDLING
|
|
121
|
+
// ============================================
|
|
122
|
+
program.on("command:*", () => {
|
|
123
|
+
console.error(chalk.red(`Unknown command: ${program.args.join(" ")}`));
|
|
124
|
+
console.log();
|
|
125
|
+
program.outputHelp();
|
|
126
|
+
process.exit(1);
|
|
127
|
+
});
|
|
128
|
+
// Parse and run
|
|
129
|
+
program.parse();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apps Commands
|
|
3
|
+
*
|
|
4
|
+
* - apps list: List all apps
|
|
5
|
+
* - apps create: Create a new app
|
|
6
|
+
* - link: Link current directory to an app
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* funforge apps list
|
|
10
|
+
*
|
|
11
|
+
* List all apps for the authenticated user.
|
|
12
|
+
*/
|
|
13
|
+
export declare function appsListCommand(): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* funforge apps create <name>
|
|
16
|
+
*
|
|
17
|
+
* Create a new app.
|
|
18
|
+
*/
|
|
19
|
+
export declare function appsCreateCommand(name: string, options: {
|
|
20
|
+
slug?: string;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* funforge link [appId]
|
|
24
|
+
*
|
|
25
|
+
* Link current directory to an app.
|
|
26
|
+
* If no appId provided, shows interactive selector.
|
|
27
|
+
*/
|
|
28
|
+
export declare function linkCommand(appId?: string): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=apps.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apps Commands
|
|
3
|
+
*
|
|
4
|
+
* - apps list: List all apps
|
|
5
|
+
* - apps create: Create a new app
|
|
6
|
+
* - link: Link current directory to an app
|
|
7
|
+
*/
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import ora from "ora";
|
|
10
|
+
import { ApiError, createApp, getApp, listApps } from "../api.js";
|
|
11
|
+
import { isAuthenticated } from "../credentials.js";
|
|
12
|
+
import { readConfig, updateConfig } from "../project-config.js";
|
|
13
|
+
/**
|
|
14
|
+
* funforge apps list
|
|
15
|
+
*
|
|
16
|
+
* List all apps for the authenticated user.
|
|
17
|
+
*/
|
|
18
|
+
export async function appsListCommand() {
|
|
19
|
+
requireAuth();
|
|
20
|
+
const spinner = ora("Fetching apps...").start();
|
|
21
|
+
try {
|
|
22
|
+
const { apps } = await listApps();
|
|
23
|
+
spinner.stop();
|
|
24
|
+
if (apps.length === 0) {
|
|
25
|
+
console.log(chalk.yellow("No apps found."));
|
|
26
|
+
console.log(chalk.gray("Create one with `funforge apps create <name>`"));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log(chalk.bold(`Your apps (${apps.length}):`));
|
|
30
|
+
console.log();
|
|
31
|
+
for (const app of apps) {
|
|
32
|
+
console.log(` ${chalk.cyan(app.slug)} - ${app.name}`);
|
|
33
|
+
console.log(chalk.gray(` ID: ${app.id}`));
|
|
34
|
+
console.log(chalk.gray(` Created: ${new Date(app.createdAt).toLocaleDateString()}`));
|
|
35
|
+
console.log();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
spinner.fail("Failed to fetch apps");
|
|
40
|
+
handleError(error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* funforge apps create <name>
|
|
45
|
+
*
|
|
46
|
+
* Create a new app.
|
|
47
|
+
*/
|
|
48
|
+
export async function appsCreateCommand(name, options) {
|
|
49
|
+
requireAuth();
|
|
50
|
+
const spinner = ora(`Creating app "${name}"...`).start();
|
|
51
|
+
try {
|
|
52
|
+
const { app } = await createApp({ name, slug: options.slug });
|
|
53
|
+
spinner.succeed(`App created!`);
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(` Name: ${app.name}`);
|
|
56
|
+
console.log(` Slug: ${chalk.cyan(app.slug)}`);
|
|
57
|
+
console.log(` ID: ${app.id}`);
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(chalk.gray("Link this directory with:"));
|
|
60
|
+
console.log(chalk.gray(` funforge link ${app.id}`));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
spinner.fail("Failed to create app");
|
|
64
|
+
handleError(error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* funforge link [appId]
|
|
69
|
+
*
|
|
70
|
+
* Link current directory to an app.
|
|
71
|
+
* If no appId provided, shows interactive selector.
|
|
72
|
+
*/
|
|
73
|
+
export async function linkCommand(appId) {
|
|
74
|
+
requireAuth();
|
|
75
|
+
// Check if already linked
|
|
76
|
+
const existingConfig = await readConfig();
|
|
77
|
+
if (existingConfig?.appId) {
|
|
78
|
+
console.log(chalk.yellow(`Already linked to ${existingConfig.appName ?? existingConfig.appId}`));
|
|
79
|
+
console.log(chalk.gray("Edit funforge.json to change the link."));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// If no appId, list apps and let user choose
|
|
83
|
+
if (!appId) {
|
|
84
|
+
const spinner = ora("Fetching apps...").start();
|
|
85
|
+
const { apps } = await listApps();
|
|
86
|
+
spinner.stop();
|
|
87
|
+
if (apps.length === 0) {
|
|
88
|
+
console.log(chalk.yellow("No apps found."));
|
|
89
|
+
console.log(chalk.gray("Create one with `funforge apps create <name>`"));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log(chalk.bold("Select an app to link:"));
|
|
93
|
+
console.log();
|
|
94
|
+
apps.forEach((app, i) => {
|
|
95
|
+
console.log(` ${i + 1}. ${chalk.cyan(app.slug)} - ${app.name}`);
|
|
96
|
+
});
|
|
97
|
+
console.log();
|
|
98
|
+
console.log(chalk.gray("Run: funforge link <app-id>"));
|
|
99
|
+
console.log();
|
|
100
|
+
console.log("App IDs:");
|
|
101
|
+
apps.forEach((app) => {
|
|
102
|
+
console.log(` ${app.slug}: ${app.id}`);
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Verify app exists
|
|
107
|
+
const spinner = ora("Verifying app...").start();
|
|
108
|
+
try {
|
|
109
|
+
const { app } = await getApp(appId);
|
|
110
|
+
spinner.stop();
|
|
111
|
+
// Save to funforge.json
|
|
112
|
+
await updateConfig({
|
|
113
|
+
appId: app.id,
|
|
114
|
+
appName: app.name,
|
|
115
|
+
appSlug: app.slug,
|
|
116
|
+
});
|
|
117
|
+
console.log(chalk.green(`Linked to ${app.name} (${app.slug})`));
|
|
118
|
+
console.log(chalk.gray("funforge.json created."));
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
spinner.fail("Failed to link app");
|
|
122
|
+
handleError(error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if user is authenticated, exit if not
|
|
127
|
+
*/
|
|
128
|
+
function requireAuth() {
|
|
129
|
+
if (!isAuthenticated()) {
|
|
130
|
+
console.log(chalk.red("Not authenticated."));
|
|
131
|
+
console.log(chalk.gray("Run `funforge login` first."));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Commands
|
|
3
|
+
*
|
|
4
|
+
* - login: Authenticate via device flow
|
|
5
|
+
* - logout: Clear credentials
|
|
6
|
+
* - whoami: Show current user
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* funforge login
|
|
10
|
+
*
|
|
11
|
+
* Authenticate using device auth flow.
|
|
12
|
+
* Opens browser for user to confirm authorization.
|
|
13
|
+
*/
|
|
14
|
+
export declare function loginCommand(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* funforge logout
|
|
17
|
+
*
|
|
18
|
+
* Clear stored credentials.
|
|
19
|
+
*/
|
|
20
|
+
export declare function logoutCommand(): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* funforge whoami
|
|
23
|
+
*
|
|
24
|
+
* Show current authenticated user.
|
|
25
|
+
*/
|
|
26
|
+
export declare function whoamiCommand(): Promise<void>;
|
|
27
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Commands
|
|
3
|
+
*
|
|
4
|
+
* - login: Authenticate via device flow
|
|
5
|
+
* - logout: Clear credentials
|
|
6
|
+
* - whoami: Show current user
|
|
7
|
+
*/
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import ora from "ora";
|
|
10
|
+
import { initDeviceAuth, pollDeviceAuth } from "../api.js";
|
|
11
|
+
import { getConfig } from "../config.js";
|
|
12
|
+
import { clearCredentials, getConfigPath, getCredentials, isAuthenticated, saveCredentials, } from "../credentials.js";
|
|
13
|
+
/**
|
|
14
|
+
* funforge login
|
|
15
|
+
*
|
|
16
|
+
* Authenticate using device auth flow.
|
|
17
|
+
* Opens browser for user to confirm authorization.
|
|
18
|
+
*/
|
|
19
|
+
export async function loginCommand() {
|
|
20
|
+
if (isAuthenticated()) {
|
|
21
|
+
const creds = getCredentials();
|
|
22
|
+
console.log(chalk.yellow(`Already logged in as ${creds.email ?? "unknown"}`));
|
|
23
|
+
console.log(chalk.gray("Run `funforge logout` first to switch accounts."));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const spinner = ora("Initializing authentication...").start();
|
|
27
|
+
try {
|
|
28
|
+
// Initialize device auth
|
|
29
|
+
const auth = await initDeviceAuth();
|
|
30
|
+
spinner.stop();
|
|
31
|
+
const config = getConfig();
|
|
32
|
+
const verificationUrl = `${config.authUrl}/cli/authorize?code=${auth.userCode}`;
|
|
33
|
+
// Display instructions
|
|
34
|
+
console.log();
|
|
35
|
+
console.log(chalk.bold("To authenticate, open this URL in your browser:"));
|
|
36
|
+
console.log();
|
|
37
|
+
console.log(chalk.cyan(` ${verificationUrl}`));
|
|
38
|
+
console.log();
|
|
39
|
+
console.log(chalk.gray(`Or enter this code manually: ${chalk.bold(auth.userCode)}`));
|
|
40
|
+
console.log();
|
|
41
|
+
// Try to open browser automatically
|
|
42
|
+
try {
|
|
43
|
+
const { default: open } = await import("open");
|
|
44
|
+
await open(verificationUrl);
|
|
45
|
+
console.log(chalk.gray("Browser opened automatically."));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// open package not available or failed, user will copy URL manually
|
|
49
|
+
console.log(chalk.gray("Copy and paste the URL above into your browser."));
|
|
50
|
+
}
|
|
51
|
+
// Poll for completion
|
|
52
|
+
const pollSpinner = ora("Waiting for authorization...").start();
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
const expiresAt = startTime + auth.expiresIn * 1000;
|
|
55
|
+
while (Date.now() < expiresAt) {
|
|
56
|
+
await sleep(auth.interval * 1000);
|
|
57
|
+
const result = await pollDeviceAuth(auth.deviceCode);
|
|
58
|
+
if (result.status === "authorized") {
|
|
59
|
+
pollSpinner.succeed("Authenticated!");
|
|
60
|
+
// Save credentials
|
|
61
|
+
saveCredentials({
|
|
62
|
+
apiKey: result.apiKey,
|
|
63
|
+
userId: result.userId,
|
|
64
|
+
email: result.email,
|
|
65
|
+
});
|
|
66
|
+
console.log();
|
|
67
|
+
console.log(chalk.green(`Logged in as ${result.email}`));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (result.status === "denied") {
|
|
71
|
+
pollSpinner.fail("Authorization denied");
|
|
72
|
+
console.log(chalk.red("You denied the authorization request."));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
if (result.status === "expired") {
|
|
76
|
+
pollSpinner.fail("Authorization expired");
|
|
77
|
+
console.log(chalk.red("The authorization code expired. Please try again."));
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
// status === "pending", continue polling
|
|
81
|
+
}
|
|
82
|
+
pollSpinner.fail("Authorization timed out");
|
|
83
|
+
console.log(chalk.red("Authorization timed out. Please try again."));
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
spinner.fail("Authentication failed");
|
|
88
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* funforge logout
|
|
94
|
+
*
|
|
95
|
+
* Clear stored credentials.
|
|
96
|
+
*/
|
|
97
|
+
export async function logoutCommand() {
|
|
98
|
+
if (!isAuthenticated()) {
|
|
99
|
+
console.log(chalk.yellow("Not logged in."));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const creds = getCredentials();
|
|
103
|
+
clearCredentials();
|
|
104
|
+
console.log(chalk.green(`Logged out from ${creds.email ?? "account"}`));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* funforge whoami
|
|
108
|
+
*
|
|
109
|
+
* Show current authenticated user.
|
|
110
|
+
*/
|
|
111
|
+
export async function whoamiCommand() {
|
|
112
|
+
if (!isAuthenticated()) {
|
|
113
|
+
console.log(chalk.yellow("Not logged in."));
|
|
114
|
+
console.log(chalk.gray("Run `funforge login` to authenticate."));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
const creds = getCredentials();
|
|
118
|
+
console.log(chalk.bold("Logged in as:"));
|
|
119
|
+
console.log(` Email: ${creds.email ?? "unknown"}`);
|
|
120
|
+
console.log(` User ID: ${creds.userId ?? "unknown"}`);
|
|
121
|
+
console.log(` Since: ${creds.savedAt ?? "unknown"}`);
|
|
122
|
+
console.log();
|
|
123
|
+
console.log(chalk.gray(`Config: ${getConfigPath()}`));
|
|
124
|
+
}
|
|
125
|
+
function sleep(ms) {
|
|
126
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
127
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Commands
|
|
3
|
+
*
|
|
4
|
+
* Sync build settings between local funforge.json and server.
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* - funforge config push - Push local settings to server
|
|
8
|
+
* - funforge config pull - Pull server settings to local file
|
|
9
|
+
* - funforge config show - Show comparison between local and server
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* funforge config push
|
|
13
|
+
*
|
|
14
|
+
* Push local funforge.json build settings to server.
|
|
15
|
+
* Local settings overwrite server settings.
|
|
16
|
+
*/
|
|
17
|
+
export declare function configPushCommand(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* funforge config pull
|
|
20
|
+
*
|
|
21
|
+
* Pull server build settings to local funforge.json.
|
|
22
|
+
* Server settings are written to local file.
|
|
23
|
+
*/
|
|
24
|
+
export declare function configPullCommand(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* funforge config show
|
|
27
|
+
*
|
|
28
|
+
* Show comparison between local funforge.json and server settings.
|
|
29
|
+
*/
|
|
30
|
+
export declare function configShowCommand(): Promise<void>;
|
|
31
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +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"}
|