@seedvault/cli 0.2.1 → 0.3.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/dist/sv.js +50 -46
- package/package.json +1 -1
package/dist/sv.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
|
+
// src/index.ts
|
|
5
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
6
|
+
import { resolve as resolve6 } from "path";
|
|
7
|
+
|
|
4
8
|
// src/commands/init.ts
|
|
5
9
|
import * as readline from "readline/promises";
|
|
6
10
|
import { stdout } from "process";
|
|
@@ -191,23 +195,23 @@ function createClient(serverUrl, token) {
|
|
|
191
195
|
const res = await request("GET", "/v1/contributors");
|
|
192
196
|
return res.json();
|
|
193
197
|
},
|
|
194
|
-
async putFile(
|
|
195
|
-
const res = await request("PUT", `/v1/contributors/${
|
|
198
|
+
async putFile(username, path, content) {
|
|
199
|
+
const res = await request("PUT", `/v1/contributors/${username}/files/${encodePath(path)}`, {
|
|
196
200
|
body: content,
|
|
197
201
|
contentType: "text/markdown"
|
|
198
202
|
});
|
|
199
203
|
return res.json();
|
|
200
204
|
},
|
|
201
|
-
async deleteFile(
|
|
202
|
-
await request("DELETE", `/v1/contributors/${
|
|
205
|
+
async deleteFile(username, path) {
|
|
206
|
+
await request("DELETE", `/v1/contributors/${username}/files/${encodePath(path)}`);
|
|
203
207
|
},
|
|
204
|
-
async listFiles(
|
|
208
|
+
async listFiles(username, prefix) {
|
|
205
209
|
const qs = prefix ? `?prefix=${encodeURIComponent(prefix)}` : "";
|
|
206
|
-
const res = await request("GET", `/v1/contributors/${
|
|
210
|
+
const res = await request("GET", `/v1/contributors/${username}/files${qs}`);
|
|
207
211
|
return res.json();
|
|
208
212
|
},
|
|
209
|
-
async getFile(
|
|
210
|
-
const res = await request("GET", `/v1/contributors/${
|
|
213
|
+
async getFile(username, path) {
|
|
214
|
+
const res = await request("GET", `/v1/contributors/${username}/files/${encodePath(path)}`);
|
|
211
215
|
return res.text();
|
|
212
216
|
},
|
|
213
217
|
async health() {
|
|
@@ -233,21 +237,21 @@ async function init(args) {
|
|
|
233
237
|
console.error(`Could not reach server at ${flags.server}`);
|
|
234
238
|
process.exit(1);
|
|
235
239
|
}
|
|
236
|
-
const
|
|
237
|
-
if (!
|
|
238
|
-
console.error("When using --token, also pass --
|
|
240
|
+
const username = flags["username"] || "";
|
|
241
|
+
if (!username) {
|
|
242
|
+
console.error("When using --token, also pass --username");
|
|
239
243
|
process.exit(1);
|
|
240
244
|
}
|
|
241
245
|
const config = {
|
|
242
246
|
server: flags.server,
|
|
243
247
|
token: flags.token,
|
|
244
|
-
|
|
248
|
+
username,
|
|
245
249
|
collections: []
|
|
246
250
|
};
|
|
247
251
|
saveConfig(config);
|
|
248
252
|
console.log("Seedvault configured.");
|
|
249
253
|
console.log(` Server: ${config.server}`);
|
|
250
|
-
console.log(`
|
|
254
|
+
console.log(` Username: ${config.username}`);
|
|
251
255
|
return;
|
|
252
256
|
}
|
|
253
257
|
if (flags.server && flags.name) {
|
|
@@ -262,13 +266,13 @@ async function init(args) {
|
|
|
262
266
|
const config = {
|
|
263
267
|
server: flags.server,
|
|
264
268
|
token: result.token,
|
|
265
|
-
|
|
269
|
+
username: result.contributor.username,
|
|
266
270
|
collections: []
|
|
267
271
|
};
|
|
268
272
|
saveConfig(config);
|
|
269
273
|
console.log("Signed up and configured.");
|
|
270
274
|
console.log(` Server: ${config.server}`);
|
|
271
|
-
console.log(`
|
|
275
|
+
console.log(` Username: ${result.contributor.username}`);
|
|
272
276
|
console.log(` Token: ${result.token}`);
|
|
273
277
|
return;
|
|
274
278
|
}
|
|
@@ -300,25 +304,24 @@ async function init(args) {
|
|
|
300
304
|
const hasToken = await rl.question("Do you already have a token? (y/N): ");
|
|
301
305
|
if (hasToken.toLowerCase() === "y") {
|
|
302
306
|
const token = await rl.question("Token: ");
|
|
303
|
-
const
|
|
304
|
-
const config = { server, token: token.trim(),
|
|
307
|
+
const username = await rl.question("Username: ");
|
|
308
|
+
const config = { server, token: token.trim(), username: username.trim(), collections: [] };
|
|
305
309
|
saveConfig(config);
|
|
306
310
|
console.log(`
|
|
307
311
|
Seedvault configured.`);
|
|
308
312
|
} else {
|
|
309
|
-
const name = await rl.question("
|
|
313
|
+
const name = await rl.question("Username (e.g. your-name-notes): ");
|
|
310
314
|
const invite = await rl.question("Invite code (leave blank if first user): ");
|
|
311
315
|
const result = await client.signup(name.trim(), invite.trim() || undefined);
|
|
312
316
|
const config = {
|
|
313
317
|
server,
|
|
314
318
|
token: result.token,
|
|
315
|
-
|
|
319
|
+
username: result.contributor.username,
|
|
316
320
|
collections: []
|
|
317
321
|
};
|
|
318
322
|
saveConfig(config);
|
|
319
323
|
console.log(`
|
|
320
|
-
Signed up as '${result.contributor.
|
|
321
|
-
console.log(` Contributor ID: ${result.contributor.id}`);
|
|
324
|
+
Signed up as '${result.contributor.username}'.`);
|
|
322
325
|
console.log(` Token: ${result.token}`);
|
|
323
326
|
console.log(`
|
|
324
327
|
Save your token \u2014 it won't be shown again.`);
|
|
@@ -2095,9 +2098,9 @@ class RetryQueue {
|
|
|
2095
2098
|
const op = this.items[0];
|
|
2096
2099
|
try {
|
|
2097
2100
|
if (op.type === "put" && op.content !== null) {
|
|
2098
|
-
await this.client.putFile(op.
|
|
2101
|
+
await this.client.putFile(op.username, op.serverPath, op.content);
|
|
2099
2102
|
} else if (op.type === "delete") {
|
|
2100
|
-
await this.client.deleteFile(op.
|
|
2103
|
+
await this.client.deleteFile(op.username, op.serverPath);
|
|
2101
2104
|
}
|
|
2102
2105
|
this.items.shift();
|
|
2103
2106
|
this.backoff = MIN_BACKOFF;
|
|
@@ -2125,13 +2128,13 @@ class RetryQueue {
|
|
|
2125
2128
|
// src/daemon/syncer.ts
|
|
2126
2129
|
class Syncer {
|
|
2127
2130
|
client;
|
|
2128
|
-
|
|
2131
|
+
username;
|
|
2129
2132
|
collections;
|
|
2130
2133
|
queue;
|
|
2131
2134
|
log;
|
|
2132
2135
|
constructor(opts) {
|
|
2133
2136
|
this.client = opts.client;
|
|
2134
|
-
this.
|
|
2137
|
+
this.username = opts.username;
|
|
2135
2138
|
this.collections = opts.collections;
|
|
2136
2139
|
this.log = opts.onLog;
|
|
2137
2140
|
this.queue = new RetryQueue(opts.client, opts.onLog);
|
|
@@ -2157,7 +2160,7 @@ class Syncer {
|
|
|
2157
2160
|
let deleted = 0;
|
|
2158
2161
|
this.log(`Syncing '${collection.name}' (${collection.path})...`);
|
|
2159
2162
|
try {
|
|
2160
|
-
const { files: serverFiles } = await this.client.listFiles(this.
|
|
2163
|
+
const { files: serverFiles } = await this.client.listFiles(this.username, collection.name + "/");
|
|
2161
2164
|
const serverMap = new Map;
|
|
2162
2165
|
for (const f of serverFiles) {
|
|
2163
2166
|
serverMap.set(f.path, f.modifiedAt);
|
|
@@ -2179,12 +2182,12 @@ class Syncer {
|
|
|
2179
2182
|
}
|
|
2180
2183
|
const content = await readFile(localFile.path, "utf-8");
|
|
2181
2184
|
try {
|
|
2182
|
-
await this.client.putFile(this.
|
|
2185
|
+
await this.client.putFile(this.username, serverPath, content);
|
|
2183
2186
|
uploaded++;
|
|
2184
2187
|
} catch {
|
|
2185
2188
|
this.queue.enqueue({
|
|
2186
2189
|
type: "put",
|
|
2187
|
-
|
|
2190
|
+
username: this.username,
|
|
2188
2191
|
serverPath,
|
|
2189
2192
|
content,
|
|
2190
2193
|
queuedAt: new Date().toISOString()
|
|
@@ -2195,12 +2198,12 @@ class Syncer {
|
|
|
2195
2198
|
if (localServerPaths.has(f.path))
|
|
2196
2199
|
continue;
|
|
2197
2200
|
try {
|
|
2198
|
-
await this.client.deleteFile(this.
|
|
2201
|
+
await this.client.deleteFile(this.username, f.path);
|
|
2199
2202
|
deleted++;
|
|
2200
2203
|
} catch {
|
|
2201
2204
|
this.queue.enqueue({
|
|
2202
2205
|
type: "delete",
|
|
2203
|
-
|
|
2206
|
+
username: this.username,
|
|
2204
2207
|
serverPath: f.path,
|
|
2205
2208
|
content: null,
|
|
2206
2209
|
queuedAt: new Date().toISOString()
|
|
@@ -2218,15 +2221,15 @@ class Syncer {
|
|
|
2218
2221
|
let queued = 0;
|
|
2219
2222
|
this.log(`Removing '${collection.name}' files from server...`);
|
|
2220
2223
|
try {
|
|
2221
|
-
const { files: serverFiles } = await this.client.listFiles(this.
|
|
2224
|
+
const { files: serverFiles } = await this.client.listFiles(this.username, collection.name + "/");
|
|
2222
2225
|
for (const f of serverFiles) {
|
|
2223
2226
|
try {
|
|
2224
|
-
await this.client.deleteFile(this.
|
|
2227
|
+
await this.client.deleteFile(this.username, f.path);
|
|
2225
2228
|
deleted++;
|
|
2226
2229
|
} catch {
|
|
2227
2230
|
this.queue.enqueue({
|
|
2228
2231
|
type: "delete",
|
|
2229
|
-
|
|
2232
|
+
username: this.username,
|
|
2230
2233
|
serverPath: f.path,
|
|
2231
2234
|
content: null,
|
|
2232
2235
|
queuedAt: new Date().toISOString()
|
|
@@ -2246,7 +2249,7 @@ class Syncer {
|
|
|
2246
2249
|
this.log(`PUT ${event.serverPath} (${content.length} bytes)`);
|
|
2247
2250
|
this.queue.enqueue({
|
|
2248
2251
|
type: "put",
|
|
2249
|
-
|
|
2252
|
+
username: this.username,
|
|
2250
2253
|
serverPath: event.serverPath,
|
|
2251
2254
|
content,
|
|
2252
2255
|
queuedAt: new Date().toISOString()
|
|
@@ -2255,7 +2258,7 @@ class Syncer {
|
|
|
2255
2258
|
this.log(`DELETE ${event.serverPath}`);
|
|
2256
2259
|
this.queue.enqueue({
|
|
2257
2260
|
type: "delete",
|
|
2258
|
-
|
|
2261
|
+
username: this.username,
|
|
2259
2262
|
serverPath: event.serverPath,
|
|
2260
2263
|
content: null,
|
|
2261
2264
|
queuedAt: new Date().toISOString()
|
|
@@ -2669,7 +2672,7 @@ async function startForeground() {
|
|
|
2669
2672
|
maybeLogOverlapWarning(removedOverlappingCollections);
|
|
2670
2673
|
log("Seedvault daemon starting...");
|
|
2671
2674
|
log(` Server: ${config.server}`);
|
|
2672
|
-
log(` Contributor: ${config.
|
|
2675
|
+
log(` Contributor: ${config.username}`);
|
|
2673
2676
|
if (config.collections.length === 0) {
|
|
2674
2677
|
log(" Collections: none");
|
|
2675
2678
|
log(" Waiting for collections to be added...");
|
|
@@ -2679,7 +2682,7 @@ async function startForeground() {
|
|
|
2679
2682
|
writeFileSync2(getPidPath(), String(process.pid));
|
|
2680
2683
|
const syncer = new Syncer({
|
|
2681
2684
|
client,
|
|
2682
|
-
|
|
2685
|
+
username: config.username,
|
|
2683
2686
|
collections: config.collections,
|
|
2684
2687
|
onLog: log
|
|
2685
2688
|
});
|
|
@@ -2809,7 +2812,7 @@ async function status() {
|
|
|
2809
2812
|
console.log(`Seedvault Status
|
|
2810
2813
|
`);
|
|
2811
2814
|
console.log(` Server: ${config.server}`);
|
|
2812
|
-
console.log(` Contributor: ${config.
|
|
2815
|
+
console.log(` Contributor: ${config.username}`);
|
|
2813
2816
|
try {
|
|
2814
2817
|
const platform = detectPlatform();
|
|
2815
2818
|
const serviceName = platform === "macos" ? "launchd" : platform === "linux" ? "systemd" : "Task Scheduler";
|
|
@@ -2847,7 +2850,7 @@ async function ls(args) {
|
|
|
2847
2850
|
const config = loadConfig();
|
|
2848
2851
|
const client = createClient(config.server, config.token);
|
|
2849
2852
|
const prefix = args[0] || undefined;
|
|
2850
|
-
const { files } = await client.listFiles(config.
|
|
2853
|
+
const { files } = await client.listFiles(config.username, prefix);
|
|
2851
2854
|
if (files.length === 0) {
|
|
2852
2855
|
console.log(prefix ? `No files matching '${prefix}'.` : "No files in your contributor.");
|
|
2853
2856
|
return;
|
|
@@ -2879,7 +2882,7 @@ async function cat(args) {
|
|
|
2879
2882
|
const config = loadConfig();
|
|
2880
2883
|
const client = createClient(config.server, config.token);
|
|
2881
2884
|
try {
|
|
2882
|
-
const content = await client.getFile(config.
|
|
2885
|
+
const content = await client.getFile(config.username, filePath);
|
|
2883
2886
|
process.stdout.write(content);
|
|
2884
2887
|
} catch (e) {
|
|
2885
2888
|
if (e instanceof ApiError && e.status === 404) {
|
|
@@ -2902,9 +2905,8 @@ async function contributors() {
|
|
|
2902
2905
|
console.log(`Contributors:
|
|
2903
2906
|
`);
|
|
2904
2907
|
for (const contributor of contributors2) {
|
|
2905
|
-
const you = contributor.
|
|
2906
|
-
console.log(` ${contributor.
|
|
2907
|
-
console.log(` ID: ${contributor.id}`);
|
|
2908
|
+
const you = contributor.username === config.username ? " (you)" : "";
|
|
2909
|
+
console.log(` ${contributor.username}${you}`);
|
|
2908
2910
|
console.log(` Created: ${new Date(contributor.createdAt).toLocaleString()}`);
|
|
2909
2911
|
console.log();
|
|
2910
2912
|
}
|
|
@@ -2938,8 +2940,8 @@ Usage: sv <command> [options]
|
|
|
2938
2940
|
|
|
2939
2941
|
Setup:
|
|
2940
2942
|
init Interactive first-time setup
|
|
2941
|
-
init --server URL --token T
|
|
2942
|
-
init --server URL --name N
|
|
2943
|
+
init --server URL --token T --username U Non-interactive (existing token)
|
|
2944
|
+
init --server URL --name N Non-interactive (signup)
|
|
2943
2945
|
|
|
2944
2946
|
Collections:
|
|
2945
2947
|
add <path> [--name N] Add a collection path
|
|
@@ -2967,7 +2969,9 @@ async function main() {
|
|
|
2967
2969
|
return;
|
|
2968
2970
|
}
|
|
2969
2971
|
if (cmd === "--version" || cmd === "-v") {
|
|
2970
|
-
|
|
2972
|
+
const pkgPath = resolve6(import.meta.dirname, "..", "package.json");
|
|
2973
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
2974
|
+
console.log(pkg.version);
|
|
2971
2975
|
return;
|
|
2972
2976
|
}
|
|
2973
2977
|
try {
|