puter-cli 1.7.0 → 1.7.1
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/CHANGELOG.md +7 -0
- package/README.md +22 -1
- package/package.json +1 -1
- package/src/commands/files.js +67 -16
- package/src/commons.js +1 -1
- package/src/executor.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,15 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
#### [v1.7.1](https://github.com/HeyPuter/puter-cli/compare/v1.7.0...v1.7.1)
|
|
8
|
+
|
|
9
|
+
- fix: update command [`38ca9e8`](https://github.com/HeyPuter/puter-cli/commit/38ca9e824642cbf8b1ffdb0d2a6e5426f84c7371)
|
|
10
|
+
- docs: update README [`6e658f6`](https://github.com/HeyPuter/puter-cli/commit/6e658f6a117ee1ba206768905d53fb61c78e136d)
|
|
11
|
+
|
|
7
12
|
#### [v1.7.0](https://github.com/HeyPuter/puter-cli/compare/v1.6.1...v1.7.0)
|
|
8
13
|
|
|
14
|
+
> 16 February 2025
|
|
15
|
+
|
|
9
16
|
- feat: save auth token when login [`55b32b7`](https://github.com/HeyPuter/puter-cli/commit/55b32b7feca050902f4470f06af38f81d3299e6a)
|
|
10
17
|
- fix: create app from host shell [`30e5028`](https://github.com/HeyPuter/puter-cli/commit/30e5028d831d26349e3ae2fc8e34921693b5702c)
|
|
11
18
|
|
package/README.md
CHANGED
|
@@ -63,8 +63,10 @@ Then just follow the prompts, this command doesn't require you to log in.
|
|
|
63
63
|
#### Authentication
|
|
64
64
|
- **Login**: Log in to your Puter account.
|
|
65
65
|
```bash
|
|
66
|
-
puter login
|
|
66
|
+
puter login [--save]
|
|
67
67
|
```
|
|
68
|
+
P.S. You can add `--save` to save your authentication `token` to `.env` file as `PUTER_API_KEY` variable.
|
|
69
|
+
|
|
68
70
|
- **Logout**: Log out of your Puter account.
|
|
69
71
|
```bash
|
|
70
72
|
puter logout
|
|
@@ -127,6 +129,19 @@ Think of it as `git [push|pull]` commands, they're basically simplified equivale
|
|
|
127
129
|
```
|
|
128
130
|
P.S. These commands consider the current directory as the base path for every operation, basic wildcards are supported: e.g. `push myapp/*.html`.
|
|
129
131
|
|
|
132
|
+
- **Synchronize Files**: Bidirectional synchronization between local and remote directories.
|
|
133
|
+
```bash
|
|
134
|
+
puter> update <local_directory> <remote_directory> [--delete] [-r]
|
|
135
|
+
```
|
|
136
|
+
P.S. The `--delete` flag removes files in the remote directory that don't exist locally. The `-r` flag enables recursive synchronization of subdirectories.
|
|
137
|
+
|
|
138
|
+
#### User Information
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The addition describes the `update` command which allows for bidirectional synchronization between local and remote directories, including the optional flags for deleting files and recursive synchronization.
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
|
|
130
145
|
#### User Information
|
|
131
146
|
- **Get User Info**: Display user information.
|
|
132
147
|
```bash
|
|
@@ -157,8 +172,14 @@ P.S. Please check the help command `help apps` for more details about any argume
|
|
|
157
172
|
```bash
|
|
158
173
|
puter> app:create <name> [<directory>] [--description="My App Description"] [--url=<url>]
|
|
159
174
|
```
|
|
175
|
+
- This command works also from your system's terminal:
|
|
176
|
+
```bash
|
|
177
|
+
$> puter app:create <name> [<directory>] [--description="My App Description"] [--url=<url>]
|
|
178
|
+
```
|
|
179
|
+
|
|
160
180
|
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` directory, 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.
|
|
161
181
|
|
|
182
|
+
|
|
162
183
|
- **Update Application**: Update an application.
|
|
163
184
|
```bash
|
|
164
185
|
puter> app:update <name> <remote_dir>
|
package/package.json
CHANGED
package/src/commands/files.js
CHANGED
|
@@ -1075,28 +1075,34 @@ export async function copyFile(args = []) {
|
|
|
1075
1075
|
/**
|
|
1076
1076
|
* List all files in a local directory.
|
|
1077
1077
|
* @param {string} localDir - The local directory path.
|
|
1078
|
+
* @param {boolean} recursive - Whether to recursively list files in subdirectories
|
|
1078
1079
|
* @returns {Array} - Array of local file objects.
|
|
1079
1080
|
*/
|
|
1080
|
-
function listLocalFiles(localDir) {
|
|
1081
|
+
function listLocalFiles(localDir, recursive = false) {
|
|
1081
1082
|
const files = [];
|
|
1082
|
-
const walkDir = (dir) => {
|
|
1083
|
+
const walkDir = (dir, baseDir) => {
|
|
1084
|
+
|
|
1083
1085
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1084
1086
|
for (const entry of entries) {
|
|
1085
1087
|
const fullPath = path.join(dir, entry.name);
|
|
1088
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
1086
1089
|
if (entry.isDirectory()) {
|
|
1087
|
-
|
|
1090
|
+
if (recursive) {
|
|
1091
|
+
walkDir(fullPath, baseDir); // Recursively traverse directories if flag is set
|
|
1092
|
+
}
|
|
1088
1093
|
} else {
|
|
1089
1094
|
files.push({
|
|
1090
|
-
relativePath:
|
|
1095
|
+
relativePath: relativePath,
|
|
1091
1096
|
localPath: fullPath,
|
|
1092
1097
|
size: fs.statSync(fullPath).size,
|
|
1093
1098
|
modified: fs.statSync(fullPath).mtime.getTime()
|
|
1094
1099
|
});
|
|
1095
1100
|
}
|
|
1096
1101
|
}
|
|
1102
|
+
|
|
1097
1103
|
};
|
|
1098
1104
|
|
|
1099
|
-
walkDir(localDir);
|
|
1105
|
+
walkDir(localDir, localDir);
|
|
1100
1106
|
return files;
|
|
1101
1107
|
}
|
|
1102
1108
|
|
|
@@ -1190,12 +1196,40 @@ async function resolveLocalDirectory(localPath) {
|
|
|
1190
1196
|
return absolutePath;
|
|
1191
1197
|
}
|
|
1192
1198
|
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Ensure a remote directory exists, creating it if necessary
|
|
1202
|
+
* @param {string} remotePath - The remote directory path
|
|
1203
|
+
*/
|
|
1204
|
+
async function ensureRemoteDirectoryExists(remotePath) {
|
|
1205
|
+
try {
|
|
1206
|
+
const exists = await pathExists(remotePath);
|
|
1207
|
+
if (!exists) {
|
|
1208
|
+
// Create the directory and any missing parents
|
|
1209
|
+
await fetch(`${API_BASE}/mkdir`, {
|
|
1210
|
+
method: 'POST',
|
|
1211
|
+
headers: getHeaders(),
|
|
1212
|
+
body: JSON.stringify({
|
|
1213
|
+
parent: path.dirname(remotePath),
|
|
1214
|
+
path: path.basename(remotePath),
|
|
1215
|
+
overwrite: false,
|
|
1216
|
+
dedupe_name: true,
|
|
1217
|
+
create_missing_parents: true
|
|
1218
|
+
})
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
console.error(chalk.red(`Failed to create remote directory: ${remotePath}`));
|
|
1223
|
+
throw error;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1193
1227
|
/**
|
|
1194
1228
|
* Synchronize a local directory with a remote directory on Puter.
|
|
1195
|
-
* @param {string[]} args - Command-line arguments (e.g., [localDir, remoteDir]).
|
|
1229
|
+
* @param {string[]} args - Command-line arguments (e.g., [localDir, remoteDir, --delete, -r]).
|
|
1196
1230
|
*/
|
|
1197
1231
|
export async function syncDirectory(args = []) {
|
|
1198
|
-
const usageMessage = 'Usage: update <local_directory> <remote_directory> [--delete]';
|
|
1232
|
+
const usageMessage = 'Usage: update <local_directory> <remote_directory> [--delete] [-r]';
|
|
1199
1233
|
if (args.length < 2) {
|
|
1200
1234
|
console.log(chalk.red(usageMessage));
|
|
1201
1235
|
return;
|
|
@@ -1204,10 +1238,12 @@ export async function syncDirectory(args = []) {
|
|
|
1204
1238
|
let localDir = '';
|
|
1205
1239
|
let remoteDir = '';
|
|
1206
1240
|
let deleteFlag = '';
|
|
1241
|
+
let recursiveFlag = false;
|
|
1207
1242
|
try {
|
|
1208
1243
|
localDir = await resolveLocalDirectory(args[0]);
|
|
1209
1244
|
remoteDir = resolvePath(getCurrentDirectory(), args[1]);
|
|
1210
1245
|
deleteFlag = args.includes('--delete'); // Whether to delete extra files
|
|
1246
|
+
recursiveFlag = args.includes('-r'); // Whether to recursively process subdirectories
|
|
1211
1247
|
} catch (error) {
|
|
1212
1248
|
console.error(chalk.red(error.message));
|
|
1213
1249
|
console.log(chalk.green(usageMessage));
|
|
@@ -1231,14 +1267,17 @@ export async function syncDirectory(args = []) {
|
|
|
1231
1267
|
}
|
|
1232
1268
|
|
|
1233
1269
|
// Step 3: List local files
|
|
1234
|
-
const localFiles = listLocalFiles(localDir);
|
|
1270
|
+
const localFiles = listLocalFiles(localDir, recursiveFlag);
|
|
1235
1271
|
|
|
1236
1272
|
// Step 4: Compare local and remote files
|
|
1237
1273
|
let { toUpload, toDownload, toDelete } = compareFiles(localFiles, remoteFiles, localDir, remoteDir);
|
|
1274
|
+
let filteredToUpload = [...toUpload];
|
|
1275
|
+
let filteredToDownload = [...toDownload];
|
|
1238
1276
|
|
|
1239
1277
|
// Step 5: Handle conflicts (if any)
|
|
1240
1278
|
const conflicts = findConflicts(toUpload, toDownload);
|
|
1241
1279
|
if (conflicts.length > 0) {
|
|
1280
|
+
|
|
1242
1281
|
console.log(chalk.yellow('The following files have conflicts:'));
|
|
1243
1282
|
conflicts.forEach(file => console.log(chalk.dim(`- ${file}`)));
|
|
1244
1283
|
|
|
@@ -1256,12 +1295,12 @@ export async function syncDirectory(args = []) {
|
|
|
1256
1295
|
]);
|
|
1257
1296
|
|
|
1258
1297
|
if (resolve === 'local') {
|
|
1259
|
-
|
|
1298
|
+
filteredToDownload = filteredToDownload.filter(file => !conflicts.includes(file.relativePath));
|
|
1260
1299
|
} else if (resolve === 'remote') {
|
|
1261
|
-
|
|
1300
|
+
filteredToUpload = filteredToUpload.filter(file => !conflicts.includes(file.relativePath));
|
|
1262
1301
|
} else {
|
|
1263
|
-
|
|
1264
|
-
|
|
1302
|
+
filteredToUpload = filteredToUpload.filter(file => !conflicts.includes(file.relativePath));
|
|
1303
|
+
filteredToDownload = filteredToDownload.filter(file => !conflicts.includes(file.relativePath));
|
|
1265
1304
|
}
|
|
1266
1305
|
}
|
|
1267
1306
|
|
|
@@ -1269,18 +1308,30 @@ export async function syncDirectory(args = []) {
|
|
|
1269
1308
|
console.log(chalk.green('Starting synchronization...'));
|
|
1270
1309
|
|
|
1271
1310
|
// Upload new/updated files
|
|
1272
|
-
for (const file of
|
|
1311
|
+
for (const file of filteredToUpload) {
|
|
1273
1312
|
console.log(chalk.cyan(`Uploading "${file.relativePath}"...`));
|
|
1274
1313
|
const dedupeName = 'false';
|
|
1275
1314
|
const overwrite = 'true';
|
|
1276
|
-
|
|
1315
|
+
|
|
1316
|
+
// Create parent directories if needed
|
|
1317
|
+
const remoteFilePath = path.join(remoteDir, file.relativePath);
|
|
1318
|
+
const remoteFileDir = path.dirname(remoteFilePath);
|
|
1319
|
+
|
|
1320
|
+
// Ensure remote directory exists
|
|
1321
|
+
await ensureRemoteDirectoryExists(remoteFileDir);
|
|
1322
|
+
|
|
1323
|
+
await uploadFile([file.localPath, remoteFileDir, dedupeName, overwrite]);
|
|
1277
1324
|
}
|
|
1278
1325
|
|
|
1279
1326
|
// Download new/updated files
|
|
1280
|
-
for (const file of
|
|
1327
|
+
for (const file of filteredToDownload) {
|
|
1281
1328
|
console.log(chalk.cyan(`Downloading "${file.relativePath}"...`));
|
|
1282
1329
|
const overwrite = 'true';
|
|
1283
|
-
|
|
1330
|
+
// Create local parent directories if needed
|
|
1331
|
+
const localFilePath = path.join(localDir, file.relativePath);
|
|
1332
|
+
// const localFileDir = path.dirname(localFilePath);
|
|
1333
|
+
|
|
1334
|
+
await downloadFile([file.remotePath, localFilePath, overwrite]);
|
|
1284
1335
|
}
|
|
1285
1336
|
|
|
1286
1337
|
// Delete extra files (if --delete flag is set)
|
package/src/commons.js
CHANGED
|
@@ -106,7 +106,7 @@ export function showDiskSpaceUsage(data) {
|
|
|
106
106
|
console.log(chalk.cyan(`Usage Percentage: `) + chalk.white(`${usagePercentage.toFixed(2)}%`));
|
|
107
107
|
console.log(chalk.dim('----------------------------------------'));
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
/**
|
|
111
111
|
* Resolve a relative path to an absolute path
|
|
112
112
|
* @param {string} currentPath - The current working directory
|
package/src/executor.js
CHANGED
|
@@ -323,7 +323,7 @@ function showHelp(command) {
|
|
|
323
323
|
Example: pull /path/to/file
|
|
324
324
|
`,
|
|
325
325
|
update: `
|
|
326
|
-
${chalk.cyan('update <src> <dest>')}
|
|
326
|
+
${chalk.cyan('update <src> <dest> [--delete] [-r]')}
|
|
327
327
|
Sync local directory with remote cloud.
|
|
328
328
|
Example: update /local/path /remote/path
|
|
329
329
|
`,
|