puter-cli 1.8.5 → 2.0.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/.github/workflows/npm-build.yml +4 -3
- package/CHANGELOG.md +38 -0
- package/README.md +18 -4
- package/bin/index.js +184 -31
- package/package.json +12 -12
- package/src/commands/apps.js +53 -139
- package/src/commands/auth.js +113 -115
- package/src/commands/deploy.js +29 -27
- package/src/commands/files.js +151 -512
- package/src/commands/shell.js +13 -25
- package/src/commands/sites.js +25 -83
- package/src/commands/subdomains.js +46 -55
- package/src/commons.js +2 -2
- package/src/executor.js +27 -28
- package/src/modules/ErrorModule.js +18 -31
- package/src/modules/ProfileModule.js +183 -123
- package/src/modules/PuterModule.js +30 -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 +69 -51
- package/tests/shell.test.js +184 -0
- package/tests/sites.test.js +67 -0
- package/tests/subdomains.test.js +90 -0
- package/src/modules/SetContextModule.js +0 -5
- package/src/temporary/context_helpers.js +0 -17
package/src/commands/apps.js
CHANGED
|
@@ -9,6 +9,7 @@ import { deleteSite } from './sites.js';
|
|
|
9
9
|
import { copyFile, createFile, listRemoteFiles, pathExists, removeFileOrDirectory } from './files.js';
|
|
10
10
|
import { getCurrentDirectory } from './auth.js';
|
|
11
11
|
import crypto from '../crypto.js';
|
|
12
|
+
import { getPuter } from '../modules/PuterModule.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* List all apps
|
|
@@ -23,22 +24,13 @@ import crypto from '../crypto.js';
|
|
|
23
24
|
*/
|
|
24
25
|
export async function listApps({ statsPeriod = 'all', iconSize = 64 } = {}) {
|
|
25
26
|
console.log(chalk.green(`Listing of apps during period "${chalk.cyan(statsPeriod)}" (try also: today, yesterday, 7d, 30d, this_month, last_month):\n`));
|
|
27
|
+
const puter = getPuter();
|
|
26
28
|
try {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
body: JSON.stringify({
|
|
31
|
-
interface: "puter-apps",
|
|
32
|
-
method: "select",
|
|
33
|
-
args: {
|
|
34
|
-
params: { icon_size: iconSize },
|
|
35
|
-
predicate: ["user-can-edit"],
|
|
36
|
-
stats_period: statsPeriod,
|
|
37
|
-
}
|
|
38
|
-
})
|
|
29
|
+
const result = await puter.apps.list({
|
|
30
|
+
icon_size: iconSize,
|
|
31
|
+
stats_period: statsPeriod
|
|
39
32
|
});
|
|
40
|
-
|
|
41
|
-
if (data && data['result']) {
|
|
33
|
+
if (result) {
|
|
42
34
|
// Create a new table instance
|
|
43
35
|
const table = new Table({
|
|
44
36
|
head: [
|
|
@@ -57,7 +49,7 @@ export async function listApps({ statsPeriod = 'all', iconSize = 64 } = {}) {
|
|
|
57
49
|
|
|
58
50
|
// Populate the table with app data
|
|
59
51
|
let i = 0;
|
|
60
|
-
for (const app of
|
|
52
|
+
for (const app of result) {
|
|
61
53
|
table.push([
|
|
62
54
|
i++,
|
|
63
55
|
app['title'],
|
|
@@ -72,7 +64,7 @@ export async function listApps({ statsPeriod = 'all', iconSize = 64 } = {}) {
|
|
|
72
64
|
|
|
73
65
|
// Display the table
|
|
74
66
|
console.log(table.toString());
|
|
75
|
-
console.log(chalk.green(`You have in total: ${chalk.cyan(
|
|
67
|
+
console.log(chalk.green(`You have in total: ${chalk.cyan(result.length)} application(s).`));
|
|
76
68
|
} else {
|
|
77
69
|
console.error(chalk.red('Unable to list your apps. Please check your credentials.'));
|
|
78
70
|
}
|
|
@@ -97,24 +89,12 @@ export async function appInfo(args = []) {
|
|
|
97
89
|
}
|
|
98
90
|
const appName = args[0].trim()
|
|
99
91
|
console.log(chalk.green(`Looking for "${chalk.dim(appName)}" app informations:\n`));
|
|
92
|
+
const puter = getPuter();
|
|
100
93
|
try {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
headers: getHeaders(),
|
|
104
|
-
body: JSON.stringify({
|
|
105
|
-
interface: "puter-apps",
|
|
106
|
-
method: "read",
|
|
107
|
-
args: {
|
|
108
|
-
id: {
|
|
109
|
-
name: appName
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
});
|
|
114
|
-
const data = await response.json();
|
|
115
|
-
if (data && data['result']) {
|
|
94
|
+
const result = await puter.apps.get(appName);
|
|
95
|
+
if (result) {
|
|
116
96
|
// Display the informations
|
|
117
|
-
displayNonNullValues(
|
|
97
|
+
displayNonNullValues(result);
|
|
118
98
|
} else {
|
|
119
99
|
console.error(chalk.red('Could not find this app.'));
|
|
120
100
|
}
|
|
@@ -150,40 +130,24 @@ export async function createApp(args) {
|
|
|
150
130
|
console.log(chalk.dim(`Description: ${description}`));
|
|
151
131
|
console.log(chalk.dim(`URL: ${url}`));
|
|
152
132
|
|
|
133
|
+
const puter = getPuter();
|
|
153
134
|
try {
|
|
154
135
|
// Step 1: Create the app
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
object: {
|
|
163
|
-
name: name,
|
|
164
|
-
index_url: url,
|
|
165
|
-
title: name,
|
|
166
|
-
description: description,
|
|
167
|
-
maximize_on_start: false,
|
|
168
|
-
background: false,
|
|
169
|
-
metadata: {
|
|
170
|
-
window_resizable: true
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
options: {
|
|
174
|
-
dedupe_name: true
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
})
|
|
136
|
+
const createAppData = await puter.apps.create({
|
|
137
|
+
name: name,
|
|
138
|
+
indexURL: url,
|
|
139
|
+
title: name,
|
|
140
|
+
description: description,
|
|
141
|
+
maximizeOnStart: false,
|
|
142
|
+
dedupeName: true
|
|
178
143
|
});
|
|
179
|
-
|
|
180
|
-
if (!createAppData || !createAppData.success) {
|
|
144
|
+
if (!createAppData) {
|
|
181
145
|
console.error(chalk.red(`Failed to create app "${name}"`));
|
|
182
146
|
return;
|
|
183
147
|
}
|
|
184
|
-
const appUid = createAppData.
|
|
185
|
-
const appName = createAppData.
|
|
186
|
-
const username = createAppData.
|
|
148
|
+
const appUid = createAppData.uid;
|
|
149
|
+
const appName = createAppData.name;
|
|
150
|
+
const username = createAppData.owner.username;
|
|
187
151
|
console.log(chalk.green(`App "${chalk.dim(name)}" created successfully!`));
|
|
188
152
|
console.log(chalk.cyan(`AppName: ${chalk.dim(appName)}\nUID: ${chalk.dim(appUid)}\nUsername: ${chalk.dim(username)}`));
|
|
189
153
|
|
|
@@ -191,18 +155,11 @@ export async function createApp(args) {
|
|
|
191
155
|
const uid = crypto.randomUUID();
|
|
192
156
|
const appDir = `/${username}/AppData/${appUid}`;
|
|
193
157
|
console.log(chalk.green(`Creating directory...\nPath: ${chalk.dim(appDir)}\nApp: ${chalk.dim(name)}\nUID: ${chalk.dim(uid)}\n`));
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
path: `app-${uid}`,
|
|
200
|
-
overwrite: true,
|
|
201
|
-
dedupe_name: false,
|
|
202
|
-
create_missing_parents: true
|
|
203
|
-
})
|
|
204
|
-
});
|
|
205
|
-
const createDirData = await createDirResponse.json();
|
|
158
|
+
const createDirData = await puter.fs.mkdir(`${appDir}/app-${uid}`, {
|
|
159
|
+
overwrite: true,
|
|
160
|
+
dedupeName: false,
|
|
161
|
+
createMissingParents: true,
|
|
162
|
+
})
|
|
206
163
|
if (!createDirData || !createDirData.uid) {
|
|
207
164
|
console.error(chalk.red(`Failed to create directory for app "${name}"`));
|
|
208
165
|
return;
|
|
@@ -246,23 +203,11 @@ export async function createApp(args) {
|
|
|
246
203
|
|
|
247
204
|
// Step 5: Update the app's index_url to point to the subdomain
|
|
248
205
|
console.log(chalk.green(`Set "${chalk.dim(subdomainName)}" as a subdomain for app: "${chalk.dim(appName)}"...\n`));
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
method: "update",
|
|
255
|
-
args: {
|
|
256
|
-
id: { name: appName },
|
|
257
|
-
object: {
|
|
258
|
-
index_url: `https://${subdomainName}.puter.site`,
|
|
259
|
-
title: name
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
})
|
|
263
|
-
});
|
|
264
|
-
const updateAppData = await updateAppResponse.json();
|
|
265
|
-
if (!updateAppData || !updateAppData.success) {
|
|
206
|
+
const updateAppData = await puter.apps.update(appName, {
|
|
207
|
+
indexURL: `https://${subdomainName}.puter.site`,
|
|
208
|
+
title: name
|
|
209
|
+
})
|
|
210
|
+
if (!updateAppData) {
|
|
266
211
|
console.error(chalk.red(`Failed to update app "${name}" with new subdomain`));
|
|
267
212
|
return;
|
|
268
213
|
}
|
|
@@ -301,35 +246,25 @@ export async function updateApp(args = []) {
|
|
|
301
246
|
return;
|
|
302
247
|
}
|
|
303
248
|
|
|
249
|
+
const puter = getPuter();
|
|
304
250
|
console.log(chalk.green(`Updating app: "${chalk.dim(name)}" from directory: ${chalk.dim(remoteDir)}\n`));
|
|
305
251
|
try {
|
|
306
252
|
// Step 1: Get the app info
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
headers: getHeaders(),
|
|
310
|
-
body: JSON.stringify({
|
|
311
|
-
interface: "puter-apps",
|
|
312
|
-
method: "read",
|
|
313
|
-
args: {
|
|
314
|
-
id: { name }
|
|
315
|
-
}
|
|
316
|
-
})
|
|
317
|
-
});
|
|
318
|
-
const data = await appResponse.json();
|
|
319
|
-
if (!data || !data.success) {
|
|
253
|
+
const data = await puter.apps.get(name);
|
|
254
|
+
if (!data) {
|
|
320
255
|
console.error(chalk.red(`Failed to find app: "${name}"`));
|
|
321
256
|
return;
|
|
322
257
|
}
|
|
323
|
-
const appUid = data.
|
|
324
|
-
const appName = data.
|
|
325
|
-
const username = data.
|
|
326
|
-
const indexUrl = data.
|
|
258
|
+
const appUid = data.uid;
|
|
259
|
+
const appName = data.name;
|
|
260
|
+
const username = data.owner.username;
|
|
261
|
+
const indexUrl = data.index_url;
|
|
327
262
|
const appDir = `/${username}/AppData/${appUid}`;
|
|
328
263
|
console.log(chalk.cyan(`AppName: ${chalk.dim(appName)}\nUID: ${chalk.dim(appUid)}\nUsername: ${chalk.dim(username)}`));
|
|
329
264
|
|
|
330
265
|
// Step 2: Find the path from subdomain
|
|
331
266
|
const subdomains = await getSubdomains();
|
|
332
|
-
const appSubdomain = subdomains.
|
|
267
|
+
const appSubdomain = subdomains.find(sd => sd.root_dir?.dirname?.endsWith(appUid));
|
|
333
268
|
if (!appSubdomain){
|
|
334
269
|
console.error(chalk.red(`Sorry! We could not find the subdomain for ${chalk.cyan(name)} application.`));
|
|
335
270
|
return;
|
|
@@ -361,6 +296,7 @@ export async function updateApp(args = []) {
|
|
|
361
296
|
console.log(chalk.dim(indexUrl));
|
|
362
297
|
} catch (error) {
|
|
363
298
|
console.error(chalk.red(`Failed to update app "${name}".\nError: ${error.message}`));
|
|
299
|
+
console.error(error);
|
|
364
300
|
}
|
|
365
301
|
}
|
|
366
302
|
|
|
@@ -374,24 +310,13 @@ export async function deleteApp(name) {
|
|
|
374
310
|
console.log(chalk.red('Usage: app:delete <name>'));
|
|
375
311
|
return false;
|
|
376
312
|
}
|
|
313
|
+
const puter = getPuter();
|
|
377
314
|
console.log(chalk.green(`Checking app "${name}"...\n`));
|
|
378
315
|
try {
|
|
379
316
|
// Step 1: Read app details
|
|
380
|
-
const
|
|
381
|
-
method: 'POST',
|
|
382
|
-
headers: getHeaders(),
|
|
383
|
-
body: JSON.stringify({
|
|
384
|
-
interface: "puter-apps",
|
|
385
|
-
method: "read",
|
|
386
|
-
args: {
|
|
387
|
-
id: { name }
|
|
388
|
-
}
|
|
389
|
-
})
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
const readData = await readResponse.json();
|
|
317
|
+
const readData = await puter.apps.get(name);
|
|
393
318
|
|
|
394
|
-
if (!readData
|
|
319
|
+
if (!readData) {
|
|
395
320
|
console.log(chalk.red(`App "${chalk.bold(name)}" not found.`));
|
|
396
321
|
return false;
|
|
397
322
|
}
|
|
@@ -399,35 +324,24 @@ export async function deleteApp(name) {
|
|
|
399
324
|
// Show app details and confirm deletion
|
|
400
325
|
console.log(chalk.cyan('\nApp Details:'));
|
|
401
326
|
console.log(chalk.dim('----------------------------------------'));
|
|
402
|
-
console.log(chalk.dim(`Name: ${chalk.cyan(readData.
|
|
403
|
-
console.log(chalk.dim(`Title: ${chalk.cyan(readData.
|
|
404
|
-
console.log(chalk.dim(`Created: ${chalk.cyan(formatDate(readData.
|
|
405
|
-
console.log(chalk.dim(`URL: ${readData.
|
|
327
|
+
console.log(chalk.dim(`Name: ${chalk.cyan(readData.name)}`));
|
|
328
|
+
console.log(chalk.dim(`Title: ${chalk.cyan(readData.title)}`));
|
|
329
|
+
console.log(chalk.dim(`Created: ${chalk.cyan(formatDate(readData.created_at))}`));
|
|
330
|
+
console.log(chalk.dim(`URL: ${readData.index_url}`));
|
|
406
331
|
console.log(chalk.dim('----------------------------------------'));
|
|
407
332
|
|
|
408
333
|
// Step 2: Delete the app
|
|
409
334
|
console.log(chalk.green(`Deleting app "${chalk.red(name)}"...`));
|
|
410
|
-
const deleteResponse = await fetch(`${API_BASE}/drivers/call`, {
|
|
411
|
-
method: 'POST',
|
|
412
|
-
headers: getHeaders(),
|
|
413
|
-
body: JSON.stringify({
|
|
414
|
-
interface: "puter-apps",
|
|
415
|
-
method: "delete",
|
|
416
|
-
args: {
|
|
417
|
-
id: { name }
|
|
418
|
-
}
|
|
419
|
-
})
|
|
420
|
-
});path
|
|
421
335
|
|
|
422
|
-
const deleteData = await
|
|
423
|
-
if (!deleteData
|
|
336
|
+
const deleteData = await puter.apps.delete(name);
|
|
337
|
+
if (!deleteData) {
|
|
424
338
|
console.error(chalk.red(`Failed to delete app "${name}".\nP.S. Make sure to provide the 'name' attribute not the 'title'.`));
|
|
425
339
|
return false;
|
|
426
340
|
}
|
|
427
341
|
|
|
428
342
|
// Lookup subdomainUID then delete it
|
|
429
343
|
const subdomains = await getSubdomains();
|
|
430
|
-
const appSubdomain = subdomains.
|
|
344
|
+
const appSubdomain = subdomains.find(sd => sd.root_dir?.dirname?.endsWith(readData.uid));
|
|
431
345
|
const subdomainDeleted = await deleteSite([appSubdomain.uid]);
|
|
432
346
|
if (subdomainDeleted){
|
|
433
347
|
console.log(chalk.green(`Subdomain: ${chalk.dim(appSubdomain.uid)} deleted.`));
|
package/src/commands/auth.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
1
|
import chalk from 'chalk';
|
|
4
2
|
import Conf from 'conf';
|
|
5
3
|
import ora from 'ora';
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { get_context } from '../temporary/context_helpers.js';
|
|
4
|
+
import { PROJECT_NAME, } from '../commons.js'
|
|
5
|
+
import { getProfileModule } from '../modules/ProfileModule.js';
|
|
6
|
+
import { getPuter } from '../modules/PuterModule.js';
|
|
10
7
|
const config = new Conf({ projectName: PROJECT_NAME });
|
|
11
8
|
|
|
12
9
|
/**
|
|
13
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
|
|
14
16
|
* @returns void
|
|
15
17
|
*/
|
|
16
|
-
export async function login(
|
|
17
|
-
const profileAPI =
|
|
18
|
-
await profileAPI.switchProfileWizard();
|
|
18
|
+
export async function login(options = {}) {
|
|
19
|
+
const profileAPI = getProfileModule();
|
|
20
|
+
await profileAPI.switchProfileWizard(options);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -23,20 +25,31 @@ export async function login(args = {}, context) {
|
|
|
23
25
|
* @returns void
|
|
24
26
|
*/
|
|
25
27
|
export async function logout() {
|
|
26
|
-
|
|
28
|
+
|
|
27
29
|
let spinner;
|
|
28
30
|
try {
|
|
29
31
|
spinner = ora('Logging out from Puter...').start();
|
|
30
32
|
const token = config.get('auth_token');
|
|
31
|
-
|
|
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 {
|
|
32
49
|
spinner.info(chalk.yellow('Already logged out'));
|
|
33
|
-
return;
|
|
34
50
|
}
|
|
35
|
-
|
|
36
|
-
config.clear(); // Remove all stored data
|
|
37
|
-
spinner.succeed(chalk.green('Successfully logged out from Puter!'));
|
|
38
51
|
} catch (error) {
|
|
39
|
-
if (spinner){
|
|
52
|
+
if (spinner) {
|
|
40
53
|
spinner.fail(chalk.red('Failed to logout'));
|
|
41
54
|
}
|
|
42
55
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
@@ -45,12 +58,9 @@ export async function logout() {
|
|
|
45
58
|
|
|
46
59
|
export async function getUserInfo() {
|
|
47
60
|
console.log(chalk.green('Getting user info...\n'));
|
|
61
|
+
const puter = getPuter();
|
|
48
62
|
try {
|
|
49
|
-
const
|
|
50
|
-
method: 'GET',
|
|
51
|
-
headers: getHeaders()
|
|
52
|
-
});
|
|
53
|
-
const data = await response.json();
|
|
63
|
+
const data = await puter.auth.getUser();
|
|
54
64
|
if (data) {
|
|
55
65
|
console.log(chalk.cyan('User Information:'));
|
|
56
66
|
console.log(chalk.dim('----------------------------------------'));
|
|
@@ -80,14 +90,12 @@ export function isAuthenticated() {
|
|
|
80
90
|
}
|
|
81
91
|
|
|
82
92
|
export function getAuthToken() {
|
|
83
|
-
const
|
|
84
|
-
const profileAPI = context[ProfileAPI];
|
|
93
|
+
const profileAPI = getProfileModule();;
|
|
85
94
|
return profileAPI.getAuthToken();
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
export function getCurrentUserName() {
|
|
89
|
-
const
|
|
90
|
-
const profileAPI = context[ProfileAPI];
|
|
98
|
+
const profileAPI = getProfileModule();;
|
|
91
99
|
return profileAPI.getCurrentProfile()?.username;
|
|
92
100
|
}
|
|
93
101
|
|
|
@@ -100,98 +108,88 @@ export function getCurrentDirectory() {
|
|
|
100
108
|
*/
|
|
101
109
|
export async function getUsageInfo() {
|
|
102
110
|
console.log(chalk.green('Fetching usage information...\n'));
|
|
111
|
+
const puter = getPuter();
|
|
103
112
|
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
chalk.bold('Usage'.padEnd(10)) +
|
|
154
|
-
chalk.bold('Limit'.padEnd(10))
|
|
155
|
-
);
|
|
156
|
-
console.log(chalk.dim('----------------------------------------'));
|
|
157
|
-
for (const [app, usage] of Object.entries(data.apps)) {
|
|
158
|
-
console.log(
|
|
159
|
-
app.padEnd(30) +
|
|
160
|
-
usage.used.toString().padEnd(10) +
|
|
161
|
-
usage.available.toString().padEnd(10)
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
console.log(chalk.dim('----------------------------------------'));
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Display general usages in a table (if available)
|
|
168
|
-
if (data.usages && data.usages.length > 0) {
|
|
169
|
-
console.log(chalk.cyan('\nGeneral Usages:'));
|
|
170
|
-
console.log(chalk.dim('----------------------------------------'));
|
|
171
|
-
console.log(
|
|
172
|
-
chalk.bold('Name'.padEnd(30)) +
|
|
173
|
-
chalk.bold('Used'.padEnd(10)) +
|
|
174
|
-
chalk.bold('Available'.padEnd(10)) +
|
|
175
|
-
chalk.bold('Refill')
|
|
176
|
-
);
|
|
177
|
-
console.log(chalk.dim('----------------------------------------'));
|
|
178
|
-
data.usages.forEach(usage => {
|
|
179
|
-
console.log(
|
|
180
|
-
usage.name.padEnd(30) +
|
|
181
|
-
usage.used.toString().padEnd(10) +
|
|
182
|
-
usage.available.toString().padEnd(10) +
|
|
183
|
-
usage.refill
|
|
184
|
-
);
|
|
185
|
-
});
|
|
186
|
-
console.log(chalk.dim('----------------------------------------'));
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
console.log(chalk.dim('========================================'));
|
|
190
|
-
console.log(chalk.green('Done.'));
|
|
191
|
-
} else {
|
|
192
|
-
console.error(chalk.red('Unable to fetch usage information.'));
|
|
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)));
|
|
193
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
|
+
}
|
|
194
192
|
} catch (error) {
|
|
195
|
-
|
|
193
|
+
console.error(chalk.red(`Failed to fetch usage information.\nError: ${error.message}`));
|
|
196
194
|
}
|
|
197
195
|
}
|