puter-cli 1.1.3 → 1.1.4

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 CHANGED
@@ -148,7 +148,7 @@ P.S. Please check the help command `help apps` for more details about any argume
148
148
 
149
149
  - **Create Application**: Create a new application.
150
150
  ```bash
151
- puter app:create <name> [<remote_dir>] [--description=<description>] [--url=<url>]
151
+ puter app:create <name> [<remote_dir>] [--url=<url>]
152
152
  ```
153
153
  P.S. By default a new `index.html` with basic content will be created, but you can set a directory when you create a new application as follows: `app:create nameOfApp ./appDir`, so all files will be copied to the `AppData` directoy, you can then update your app using `app:update <name> <remote_dir>`. This command will attempt to create a subdomain with a random `uid` prefixed with the name of the app.
154
154
 
@@ -170,7 +170,7 @@ The static sites are served from the selected directory (or the current director
170
170
 
171
171
  - **Deploy Site**: Deploy a static website from a directory.
172
172
  ```bash
173
- puter site:create <dir> [--subdomain=<name>]
173
+ puter site:create <app_name> [<dir>] [--subdomain=<name>]
174
174
  ```
175
175
  P.S. If the subdomain already exists, it will generate a new random one can set your own subdomain using `--subdomain` argument.
176
176
 
package/bin/index.js CHANGED
@@ -5,7 +5,6 @@ import { init } from '../commands/init.js';
5
5
  import { startShell } from '../commands/shell.js';
6
6
  import { PROJECT_NAME, getLatestVersion } from '../commands/commons.js';
7
7
 
8
-
9
8
  async function main() {
10
9
  const { version } = await getLatestVersion(PROJECT_NAME);
11
10
 
package/commands/apps.js CHANGED
@@ -8,6 +8,7 @@ import { createSubdomain, getSubdomains } from './subdomains.js';
8
8
  import { deleteSite } from './sites.js';
9
9
  import { copyFile, createFile, listRemoteFiles, pathExists, removeFileOrDirectory } from './files.js';
10
10
  import { getCurrentDirectory } from './auth.js';
11
+ import crypto from './crypto.js';
11
12
 
12
13
  /**
13
14
  * List all apps
@@ -131,19 +132,19 @@ export async function appInfo(args = []) {
131
132
  */
132
133
  export async function createApp(args = []) {
133
134
  if (args.length < 1 || !isValidAppName(args[0])) {
134
- console.log(chalk.red('Usage: app:create <valid_name_app> [<remote_dir>] [--description=<description>] [--url=<url>]'));
135
+ console.log(chalk.red('Usage: app:create <valid_name_app> [<remote_dir>] [--url=<url>]'));
135
136
  console.log(chalk.yellow('Example: app:create myapp'));
136
137
  console.log(chalk.yellow('Example: app:create myapp ./myapp'));
137
- console.log(chalk.yellow('Example: app:create myapp --description=myapp'));
138
138
  return;
139
139
  }
140
140
  const name = args[0]; // App name (required)
141
141
  // Use the default home page if the root directory if none specified
142
142
  const localDir = (args[1] && !args[1].startsWith('--'))? resolvePath(getCurrentDirectory(), args[1]):'';
143
- const description = (args.find(arg => arg.toLocaleLowerCase().startsWith('--description='))?.split('=')[1]) || ''; // Optional description
143
+ // Optional description (disabled at the moment)
144
+ const description = ''; // (args.find(arg => arg.toLocaleLowerCase().startsWith('--description='))?.split('=')[1]) || '';
144
145
  const url = (args.find(arg => arg.toLocaleLowerCase().startsWith('--url='))?.split('=')[1]) || 'https://dev-center.puter.com/coming-soon.html'; // Optional url
145
146
 
146
- console.log(chalk.green(`Creating app: "${chalk.dim(name)}"...\n`));
147
+ console.log(chalk.dim(`Creating app: ${chalk.green(name)}...\n`));
147
148
  try {
148
149
  // Step 1: Create the app
149
150
  const createAppResponse = await fetch(`${API_BASE}/drivers/call`, {
@@ -379,7 +380,7 @@ export async function deleteApp(name) {
379
380
  const readData = await readResponse.json();
380
381
 
381
382
  if (!readData.success || !readData.result) {
382
- console.log(chalk.log(`App "${chalk.red(name)}" not found.`));
383
+ console.log(chalk.red(`App "${chalk.bold(name)}" not found.`));
383
384
  return false;
384
385
  }
385
386
 
@@ -0,0 +1,176 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ import { TextEncoder } from 'util';
3
+
4
+ class Hash {
5
+ constructor(algorithm) {
6
+ this.algorithm = algorithm;
7
+ this.data = [];
8
+ }
9
+
10
+ update(data) {
11
+ if (typeof data === 'string') {
12
+ const encoder = new TextEncoder();
13
+ data = encoder.encode(data);
14
+ }
15
+ this.data.push(Buffer.from(data));
16
+ return this;
17
+ }
18
+
19
+ async digest(encoding = 'hex') {
20
+ const concatenatedData = Buffer.concat(this.data);
21
+ const hashBuffer = await crypto.subtle.digest(
22
+ this.algorithm.toUpperCase(),
23
+ concatenatedData
24
+ );
25
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
26
+
27
+ if (encoding === 'buffer') {
28
+ return Buffer.from(hashArray);
29
+ }
30
+
31
+ const hashHex = hashArray
32
+ .map(byte => byte.toString(16).padStart(2, '0'))
33
+ .join('');
34
+
35
+ if (encoding === 'hex') return hashHex;
36
+ if (encoding === 'base64') return Buffer.from(hashHex, 'hex').toString('base64');
37
+
38
+ throw new Error(`Unsupported encoding: ${encoding}`);
39
+ }
40
+ }
41
+
42
+ class Hmac {
43
+ constructor(algorithm, key) {
44
+ this.algorithm = algorithm;
45
+ this.key = typeof key === 'string' ? Buffer.from(key) : key;
46
+ this.data = [];
47
+ }
48
+
49
+ update(data) {
50
+ if (typeof data === 'string') {
51
+ const encoder = new TextEncoder();
52
+ data = encoder.encode(data);
53
+ }
54
+ this.data.push(Buffer.from(data));
55
+ return this;
56
+ }
57
+
58
+ async digest(encoding = 'hex') {
59
+ const concatenatedData = Buffer.concat(this.data);
60
+ const key = await crypto.subtle.importKey(
61
+ 'raw',
62
+ this.key,
63
+ { name: 'HMAC', hash: { name: this.algorithm.toUpperCase() } },
64
+ false,
65
+ ['sign']
66
+ );
67
+
68
+ const signature = await crypto.subtle.sign(
69
+ 'HMAC',
70
+ key,
71
+ concatenatedData
72
+ );
73
+
74
+ const hashArray = Array.from(new Uint8Array(signature));
75
+
76
+ if (encoding === 'buffer') {
77
+ return Buffer.from(hashArray);
78
+ }
79
+
80
+ const hashHex = hashArray
81
+ .map(byte => byte.toString(16).padStart(2, '0'))
82
+ .join('');
83
+
84
+ if (encoding === 'hex') return hashHex;
85
+ if (encoding === 'base64') return Buffer.from(hashHex, 'hex').toString('base64');
86
+
87
+ throw new Error(`Unsupported encoding: ${encoding}`);
88
+ }
89
+ }
90
+
91
+ const randomBytes = (size) => {
92
+ const array = new Uint8Array(size);
93
+ crypto.getRandomValues(array);
94
+ return Buffer.from(array);
95
+ };
96
+
97
+ const createHash = (algorithm) => {
98
+ return new Hash(algorithm);
99
+ };
100
+
101
+ const createHmac = (algorithm, key) => {
102
+ return new Hmac(algorithm, key);
103
+ };
104
+
105
+ const randomUUID = () => {
106
+ return uuidv4();
107
+ };
108
+
109
+ const scrypt = async (password, salt, keylen, options = {}) => {
110
+ const encoder = new TextEncoder();
111
+ const passwordBuffer = encoder.encode(password);
112
+ const saltBuffer = encoder.encode(salt);
113
+
114
+ const N = options.N || 16384;
115
+ const r = options.r || 8;
116
+ const p = options.p || 1;
117
+
118
+ const key = await crypto.subtle.importKey(
119
+ 'raw',
120
+ passwordBuffer,
121
+ 'PBKDF2',
122
+ false,
123
+ ['deriveBits']
124
+ );
125
+
126
+ const derivedKey = await crypto.subtle.deriveBits(
127
+ {
128
+ name: 'PBKDF2',
129
+ salt: saltBuffer,
130
+ iterations: N * r * p,
131
+ hash: 'SHA-256'
132
+ },
133
+ key,
134
+ keylen * 8
135
+ );
136
+
137
+ return Buffer.from(derivedKey);
138
+ };
139
+
140
+ const pbkdf2 = async (password, salt, iterations, keylen, digest) => {
141
+ const encoder = new TextEncoder();
142
+ const passwordBuffer = encoder.encode(password);
143
+ const saltBuffer = encoder.encode(salt);
144
+
145
+ const key = await crypto.subtle.importKey(
146
+ 'raw',
147
+ passwordBuffer,
148
+ 'PBKDF2',
149
+ false,
150
+ ['deriveBits']
151
+ );
152
+
153
+ const derivedKey = await crypto.subtle.deriveBits(
154
+ {
155
+ name: 'PBKDF2',
156
+ salt: saltBuffer,
157
+ iterations,
158
+ hash: digest.toUpperCase()
159
+ },
160
+ key,
161
+ keylen * 8
162
+ );
163
+
164
+ return Buffer.from(derivedKey);
165
+ };
166
+
167
+ export default {
168
+ createHash,
169
+ createHmac,
170
+ randomBytes,
171
+ randomUUID,
172
+ scrypt,
173
+ pbkdf2,
174
+ Hash,
175
+ Hmac
176
+ };
@@ -187,7 +187,7 @@ function showHelp(command) {
187
187
  Example: app myapp
188
188
  `,
189
189
  'app:create': `
190
- ${chalk.cyan('app:create <name> [url]')}
190
+ ${chalk.cyan('app:create <name> [<remote_dir>] [--url=<url>]')}
191
191
  Create a new app.
192
192
  Example: app:create myapp https://example.com
193
193
  `,
@@ -279,9 +279,9 @@ function showHelp(command) {
279
279
  Example: site:delete sd-123456
280
280
  `,
281
281
  'site:create': `
282
- ${chalk.cyan('site:create <dir> [--subdomain=<name>]')}
282
+ ${chalk.cyan('site:create <app_name> [<dir>] [--subdomain=<name>]')}
283
283
  Create a static website from directory.
284
- Example: site:create /path/to/dir --subdomain=myapp
284
+ Example: site:create mywebsite /path/to/dir --subdomain=mywebsite
285
285
  `,
286
286
  '!': `
287
287
  ${chalk.cyan('!<command>')}
package/commands/files.js CHANGED
@@ -11,6 +11,7 @@ import { formatDate, formatDateTime, formatSize } from './utils.js';
11
11
  import inquirer from 'inquirer';
12
12
  import { getAuthToken, getCurrentDirectory, getCurrentUserName } from './auth.js';
13
13
  import { updatePrompt } from './shell.js';
14
+ import crypto from './crypto.js';
14
15
 
15
16
  const config = new Conf({ projectName: PROJECT_NAME });
16
17
 
package/commands/sites.js CHANGED
@@ -153,7 +153,7 @@ export async function infoSite(args = []) {
153
153
  // Use the current directory as the root directory if none specified
154
154
  const remoteDir = resolvePath(getCurrentDirectory(), (args[1] && !args[1].startsWith('--'))?args[1]:'.');
155
155
 
156
- console.log(chalk.green(`Creating site "${appName}" from "${remoteDir}"...\n`));
156
+ console.log(chalk.dim(`Creating site ${chalk.green(appName)} from: ${chalk.green(remoteDir)}...\n`));
157
157
  try {
158
158
  // Step 1: Determine the subdomain
159
159
  let subdomain;
@@ -182,7 +182,7 @@ export async function infoSite(args = []) {
182
182
  } else {
183
183
  console.log(chalk.yellow(`However, It's linked to different directory at: ${subdomainObj.root_dir?.path}`));
184
184
  console.log(chalk.cyan(`We'll try to unlink this subdomain from that directory...`));
185
- const result = await deleteSubdomain(subdomainObj.uid);
185
+ const result = await deleteSubdomain(subdomainObj?.uid);
186
186
  if (result) {
187
187
  console.log(chalk.green('Looks like this subdomain is free again, please try again.'));
188
188
  return;
@@ -205,8 +205,8 @@ export async function infoSite(args = []) {
205
205
  return;
206
206
  }
207
207
 
208
- console.log(chalk.green(`App "${chalk.red(appName)}" created successfully at:`));
209
- console.log(chalk.dim(`https://${site.subdomain}.puter.site`));
208
+ console.log(chalk.green(`App ${chalk.dim(appName)} created successfully and accessible at:`));
209
+ console.log(chalk.cyan(`https://${site.subdomain}.puter.site`));
210
210
  } catch (error) {
211
211
  console.error(chalk.red('Failed to create site.'));
212
212
  console.error(chalk.red(`Error: ${error.message}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "puter-cli",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Command line interface for Puter cloud platform",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -33,7 +33,8 @@
33
33
  "inquirer": "^9.2.12",
34
34
  "minimatch": "^10.0.1",
35
35
  "node-fetch": "^3.3.2",
36
- "ora": "^8.0.1"
36
+ "ora": "^8.0.1",
37
+ "uuid": "^11.0.5"
37
38
  },
38
39
  "devDependencies": {
39
40
  "vitest": "^2.1.8"