@heyputer/shell 2.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.
@@ -0,0 +1,195 @@
1
+ import chalk from 'chalk';
2
+ import Conf from 'conf';
3
+ import ora from 'ora';
4
+ import { PROJECT_NAME, } from '../commons.js'
5
+ import { getProfileModule } from '../modules/ProfileModule.js';
6
+ import { getPuter } from '../modules/PuterModule.js';
7
+ const config = new Conf({ projectName: PROJECT_NAME });
8
+
9
+ /**
10
+ * Login user
11
+ * @param {Object} options - Login options
12
+ * @param {boolean} options.save - Save token to .env file
13
+ * @param {boolean} options.web - Use browser-based login (default)
14
+ * @param {boolean} options.withCredentials - Use username/password login
15
+ * @param {string} options.host - Puter host URL
16
+ * @returns void
17
+ */
18
+ export async function login(options = {}) {
19
+ const profileAPI = getProfileModule();
20
+ await profileAPI.switchProfileWizard(options);
21
+ }
22
+
23
+ /**
24
+ * Logout user
25
+ * @returns void
26
+ */
27
+ export async function logout() {
28
+
29
+ let spinner;
30
+ try {
31
+ spinner = ora('Logging out from Puter...').start();
32
+ const token = config.get('auth_token');
33
+ const selected_profile = config.get('selected_profile');
34
+
35
+ if (token) {
36
+ // legacy auth
37
+ config.clear();
38
+ spinner.succeed(chalk.green('Successfully logged out from Puter!'));
39
+ } else if (selected_profile) {
40
+ // multi profile auth
41
+ config.delete('selected_profile');
42
+ config.delete('username');
43
+ config.delete('cwd');
44
+
45
+ const profiles = config.get('profiles');
46
+ config.set('profiles', profiles.filter(profile => profile.uuid != selected_profile));
47
+ spinner.succeed(chalk.green('Successfully logged out from Puter!'));
48
+ } else {
49
+ spinner.info(chalk.yellow('Already logged out'));
50
+ }
51
+ } catch (error) {
52
+ if (spinner) {
53
+ spinner.fail(chalk.red('Failed to logout'));
54
+ }
55
+ console.error(chalk.red(`Error: ${error.message}`));
56
+ }
57
+ }
58
+
59
+ export async function getUserInfo() {
60
+ console.log(chalk.green('Getting user info...\n'));
61
+ const puter = getPuter();
62
+ try {
63
+ const data = await puter.auth.getUser();
64
+ if (data) {
65
+ console.log(chalk.cyan('User Information:'));
66
+ console.log(chalk.dim('----------------------------------------'));
67
+ console.log(chalk.cyan(`Username: `) + chalk.white(data.username));
68
+ console.log(chalk.cyan(`UUID: `) + chalk.white(data.uuid));
69
+ console.log(chalk.cyan(`Email: `) + chalk.white(data.email));
70
+ console.log(chalk.cyan(`Email Confirmed: `) + chalk.white(data.email_confirmed ? 'Yes' : 'No'));
71
+ console.log(chalk.cyan(`Temporary Account: `) + chalk.white(data.is_temp ? 'Yes' : 'No'));
72
+ console.log(chalk.cyan(`Account Age: `) + chalk.white(data.human_readable_age));
73
+ console.log(chalk.dim('----------------------------------------'));
74
+ console.log(chalk.cyan('Feature Flags:'));
75
+ for (const [flag, enabled] of Object.entries(data.feature_flags)) {
76
+ console.log(chalk.cyan(` - ${flag}: `) + chalk.white(enabled ? 'Enabled' : 'Disabled'));
77
+ }
78
+ console.log(chalk.dim('----------------------------------------'));
79
+ console.log(chalk.green('Done.'));
80
+ } else {
81
+ console.error(chalk.red('Unable to get your info. Please check your credentials.'));
82
+ }
83
+ } catch (error) {
84
+ console.error(chalk.red(`Failed to get user info.\nError: ${error.message}`));
85
+ console.log(error);
86
+ }
87
+ }
88
+ export function isAuthenticated() {
89
+ return !!config.get('auth_token');
90
+ }
91
+
92
+ export function getAuthToken() {
93
+ const profileAPI = getProfileModule();;
94
+ return profileAPI.getAuthToken();
95
+ }
96
+
97
+ export function getCurrentUserName() {
98
+ const profileAPI = getProfileModule();;
99
+ return profileAPI.getCurrentProfile()?.username;
100
+ }
101
+
102
+ export function getCurrentDirectory() {
103
+ return config.get('cwd');
104
+ }
105
+
106
+ /**
107
+ * Fetch usage information
108
+ */
109
+ export async function getUsageInfo() {
110
+ console.log(chalk.green('Fetching usage information...\n'));
111
+ const puter = getPuter();
112
+ try {
113
+ const data = await puter.auth.getMonthlyUsage();
114
+ if (data) {
115
+ // Display allowance information
116
+ if (data.allowanceInfo) {
117
+ console.log(chalk.cyan('Allowance Information:'));
118
+ console.log(chalk.dim('='.repeat(100)));
119
+ console.log(chalk.cyan(`Month Usage Allowance: `) + chalk.white(data.allowanceInfo.monthUsageAllowance.toLocaleString()));
120
+ console.log(chalk.cyan(`Remaining: `) + chalk.white(data.allowanceInfo.remaining.toLocaleString()));
121
+ const usedPercentage = ((data.allowanceInfo.monthUsageAllowance - data.allowanceInfo.remaining) / data.allowanceInfo.monthUsageAllowance * 100).toFixed(2);
122
+ console.log(chalk.cyan(`Used: `) + chalk.white(`${usedPercentage}%`));
123
+ console.log(chalk.dim('='.repeat(100)));
124
+ }
125
+
126
+ // Display usage information per API
127
+ if (data.usage) {
128
+ console.log(chalk.cyan('\nAPI Usage:'));
129
+ console.log(chalk.dim('='.repeat(100)));
130
+ console.log(
131
+ chalk.bold('API'.padEnd(50)) +
132
+ chalk.bold('Count'.padEnd(15)) +
133
+ chalk.bold('Cost'.padEnd(20)) +
134
+ chalk.bold('Units')
135
+ );
136
+ console.log(chalk.dim('='.repeat(100)));
137
+
138
+ // Filter out 'total' and sort entries by cost (descending)
139
+ const usageEntries = Object.entries(data.usage)
140
+ .filter(([key]) => key !== 'total')
141
+ .sort(([, a], [, b]) => b.cost - a.cost);
142
+
143
+ usageEntries.forEach(([api, details]) => {
144
+ console.log(
145
+ api.padEnd(50) +
146
+ details.count.toString().padEnd(15) +
147
+ details.cost.toLocaleString().padEnd(20) +
148
+ details.units.toLocaleString()
149
+ );
150
+ });
151
+
152
+ // Display total if available
153
+ if (data.usage.total !== undefined) {
154
+ console.log(chalk.dim('='.repeat(100)));
155
+ console.log(
156
+ chalk.bold('TOTAL'.padEnd(50)) +
157
+ ''.padEnd(15) +
158
+ chalk.bold(data.usage.total.toLocaleString())
159
+ );
160
+ }
161
+ console.log(chalk.dim('='.repeat(100)));
162
+ }
163
+
164
+ // Display app totals
165
+ if (data.appTotals && Object.keys(data.appTotals).length > 0) {
166
+ console.log(chalk.cyan('\nApp Totals:'));
167
+ console.log(chalk.dim('='.repeat(100)));
168
+ console.log(
169
+ chalk.bold('App'.padEnd(50)) +
170
+ chalk.bold('Count'.padEnd(15)) +
171
+ chalk.bold('Total')
172
+ );
173
+ console.log(chalk.dim('='.repeat(100)));
174
+
175
+ // Sort by total (descending)
176
+ const appEntries = Object.entries(data.appTotals)
177
+ .sort(([, a], [, b]) => b.total - a.total);
178
+
179
+ appEntries.forEach(([app, details]) => {
180
+ console.log(
181
+ app.padEnd(50) +
182
+ details.count.toString().padEnd(15) +
183
+ details.total.toLocaleString()
184
+ );
185
+ });
186
+ console.log(chalk.dim('='.repeat(100)));
187
+ }
188
+ console.log(chalk.green('Done.'));
189
+ } else {
190
+ console.error(chalk.red('Unable to fetch usage information.'));
191
+ }
192
+ } catch (error) {
193
+ console.error(chalk.red(`Failed to fetch usage information.\nError: ${error.message}`));
194
+ }
195
+ }
@@ -0,0 +1,54 @@
1
+ import chalk from 'chalk';
2
+ import { generateAppName } from '../commons.js';
3
+ import { syncDirectory } from './files.js';
4
+ import { createSite } from './sites.js';
5
+ import { getPuter } from '../modules/PuterModule.js';
6
+
7
+ /**
8
+ * Deploy a local web project to Puter.
9
+ * @param {string[]} args - Command-line arguments (e.g., <local_dir> [--subdomain=<subdomain>]).
10
+ */
11
+ export async function deploy(args = []) {
12
+ if (args.length < 1) {
13
+ console.log(chalk.red('Usage: site:deploy <local_dir> [--subdomain=<subdomain>]'));
14
+ console.log(chalk.yellow('Example: site:deploy .'));
15
+ console.log(chalk.yellow('Example: site:deploy ./dist'));
16
+ console.log(chalk.yellow('Example: site:deploy ./dist --subdomain=my-app-new'));
17
+ return;
18
+ }
19
+ const puter = getPuter();
20
+
21
+ const sourceDirArg = args.find(arg => !arg.startsWith('--'));
22
+ const sourceDir = sourceDirArg || '.';
23
+
24
+ let subdomain = args.find(arg => arg.startsWith('--subdomain='))?.split('=')[1];
25
+ if (!subdomain) {
26
+ subdomain = generateAppName();
27
+ }
28
+
29
+ const remoteDir = `~/sites/${subdomain}/deployment`;
30
+
31
+ // this will handle the increments
32
+ const directory = await puter.fs.mkdir(remoteDir, {
33
+ dedupeName: true,
34
+ createMissingParents: true
35
+ })
36
+
37
+ console.log(chalk.cyan(`Deploying '${sourceDir}' to '${subdomain}.puter.site'...`));
38
+
39
+ try {
40
+ // 1. Upload files
41
+ await syncDirectory([sourceDir, directory.path, '--delete', '-r', '--overwrite']);
42
+
43
+ // 2. Create the site
44
+ const site = await createSite([subdomain, directory.path, `--subdomain=${subdomain}`]);
45
+
46
+ if (site) {
47
+ console.log(chalk.green('Deployment successful!'));
48
+ } else {
49
+ console.log(chalk.yellow('Deployment successfuly updated!'));
50
+ }
51
+ } catch (error) {
52
+ console.error(chalk.red(`Deployment failed: ${error.message}`));
53
+ }
54
+ }