@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.
- package/.claude/settings.local.json +9 -0
- package/.env.example +3 -0
- package/.github/workflows/npm-build.yml +34 -0
- package/.github/workflows/npm-publish.yml +50 -0
- package/.idx/dev.nix +53 -0
- package/CHANGELOG.md +292 -0
- package/LICENSE.md +21 -0
- package/README.md +18 -0
- package/bin/index.js +258 -0
- package/package.json +58 -0
- package/screenshot.png +0 -0
- package/src/commands/apps.js +356 -0
- package/src/commands/auth.js +195 -0
- package/src/commands/deploy.js +54 -0
- package/src/commands/files.js +1187 -0
- package/src/commands/init.js +322 -0
- package/src/commands/shell.js +61 -0
- package/src/commands/sites.js +169 -0
- package/src/commands/subdomains.js +95 -0
- package/src/commons.js +409 -0
- package/src/crypto.js +9 -0
- package/src/executor.js +386 -0
- package/src/modules/ErrorModule.js +20 -0
- package/src/modules/ProfileModule.js +334 -0
- package/src/modules/PuterModule.js +30 -0
- package/src/utils.js +135 -0
- package/tests/ErrorModule.test.js +42 -0
- package/tests/ProfileModule.test.js +274 -0
- package/tests/PuterModule.test.js +56 -0
- package/tests/apps.test.js +194 -0
- package/tests/commons.test.js +380 -0
- package/tests/deploy.test.js +84 -0
- package/tests/executor.test.js +52 -0
- package/tests/files.test.js +640 -0
- package/tests/login.test.js +206 -0
- package/tests/shell.test.js +184 -0
- package/tests/sites.test.js +67 -0
- package/tests/subdomains.test.js +90 -0
- package/tests/utils.test.js +193 -0
|
@@ -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
|
+
}
|