@seedvault/cli 0.2.0 → 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.
Files changed (2) hide show
  1. package/dist/sv.js +60 -48
  2. package/package.json +1 -1
package/dist/sv.js CHANGED
@@ -1,9 +1,14 @@
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
- import { stdin, stdout } from "process";
10
+ import { stdout } from "process";
11
+ import * as fs from "fs";
7
12
 
8
13
  // src/config.ts
9
14
  import { join, resolve, relative, isAbsolute } from "path";
@@ -190,23 +195,23 @@ function createClient(serverUrl, token) {
190
195
  const res = await request("GET", "/v1/contributors");
191
196
  return res.json();
192
197
  },
193
- async putFile(contributorId, path, content) {
194
- const res = await request("PUT", `/v1/contributors/${contributorId}/files/${encodePath(path)}`, {
198
+ async putFile(username, path, content) {
199
+ const res = await request("PUT", `/v1/contributors/${username}/files/${encodePath(path)}`, {
195
200
  body: content,
196
201
  contentType: "text/markdown"
197
202
  });
198
203
  return res.json();
199
204
  },
200
- async deleteFile(contributorId, path) {
201
- await request("DELETE", `/v1/contributors/${contributorId}/files/${encodePath(path)}`);
205
+ async deleteFile(username, path) {
206
+ await request("DELETE", `/v1/contributors/${username}/files/${encodePath(path)}`);
202
207
  },
203
- async listFiles(contributorId, prefix) {
208
+ async listFiles(username, prefix) {
204
209
  const qs = prefix ? `?prefix=${encodeURIComponent(prefix)}` : "";
205
- const res = await request("GET", `/v1/contributors/${contributorId}/files${qs}`);
210
+ const res = await request("GET", `/v1/contributors/${username}/files${qs}`);
206
211
  return res.json();
207
212
  },
208
- async getFile(contributorId, path) {
209
- const res = await request("GET", `/v1/contributors/${contributorId}/files/${encodePath(path)}`);
213
+ async getFile(username, path) {
214
+ const res = await request("GET", `/v1/contributors/${username}/files/${encodePath(path)}`);
210
215
  return res.text();
211
216
  },
212
217
  async health() {
@@ -232,21 +237,21 @@ async function init(args) {
232
237
  console.error(`Could not reach server at ${flags.server}`);
233
238
  process.exit(1);
234
239
  }
235
- const contributorId = flags["contributor-id"] || "";
236
- if (!contributorId) {
237
- console.error("When using --token, also pass --contributor-id");
240
+ const username = flags["username"] || "";
241
+ if (!username) {
242
+ console.error("When using --token, also pass --username");
238
243
  process.exit(1);
239
244
  }
240
245
  const config = {
241
246
  server: flags.server,
242
247
  token: flags.token,
243
- contributorId,
248
+ username,
244
249
  collections: []
245
250
  };
246
251
  saveConfig(config);
247
252
  console.log("Seedvault configured.");
248
253
  console.log(` Server: ${config.server}`);
249
- console.log(` Contributor ID: ${config.contributorId}`);
254
+ console.log(` Username: ${config.username}`);
250
255
  return;
251
256
  }
252
257
  if (flags.server && flags.name) {
@@ -261,17 +266,24 @@ async function init(args) {
261
266
  const config = {
262
267
  server: flags.server,
263
268
  token: result.token,
264
- contributorId: result.contributor.id,
269
+ username: result.contributor.username,
265
270
  collections: []
266
271
  };
267
272
  saveConfig(config);
268
273
  console.log("Signed up and configured.");
269
274
  console.log(` Server: ${config.server}`);
270
- console.log(` Contributor: ${result.contributor.name} (${result.contributor.id})`);
275
+ console.log(` Username: ${result.contributor.username}`);
271
276
  console.log(` Token: ${result.token}`);
272
277
  return;
273
278
  }
274
- const rl = readline.createInterface({ input: stdin, output: stdout });
279
+ let input;
280
+ try {
281
+ const fd = fs.openSync("/dev/tty", "r");
282
+ input = fs.createReadStream("", { fd });
283
+ } catch {
284
+ input = process.stdin;
285
+ }
286
+ const rl = readline.createInterface({ input, output: stdout });
275
287
  try {
276
288
  console.log(`Seedvault Setup
277
289
  `);
@@ -292,25 +304,24 @@ async function init(args) {
292
304
  const hasToken = await rl.question("Do you already have a token? (y/N): ");
293
305
  if (hasToken.toLowerCase() === "y") {
294
306
  const token = await rl.question("Token: ");
295
- const contributorId = await rl.question("Contributor ID: ");
296
- const config = { server, token: token.trim(), contributorId: contributorId.trim(), collections: [] };
307
+ const username = await rl.question("Username: ");
308
+ const config = { server, token: token.trim(), username: username.trim(), collections: [] };
297
309
  saveConfig(config);
298
310
  console.log(`
299
311
  Seedvault configured.`);
300
312
  } else {
301
- const name = await rl.question("Contributor name (e.g. your-name-notes): ");
313
+ const name = await rl.question("Username (e.g. your-name-notes): ");
302
314
  const invite = await rl.question("Invite code (leave blank if first user): ");
303
315
  const result = await client.signup(name.trim(), invite.trim() || undefined);
304
316
  const config = {
305
317
  server,
306
318
  token: result.token,
307
- contributorId: result.contributor.id,
319
+ username: result.contributor.username,
308
320
  collections: []
309
321
  };
310
322
  saveConfig(config);
311
323
  console.log(`
312
- Signed up as '${result.contributor.name}'.`);
313
- console.log(` Contributor ID: ${result.contributor.id}`);
324
+ Signed up as '${result.contributor.username}'.`);
314
325
  console.log(` Token: ${result.token}`);
315
326
  console.log(`
316
327
  Save your token \u2014 it won't be shown again.`);
@@ -2087,9 +2098,9 @@ class RetryQueue {
2087
2098
  const op = this.items[0];
2088
2099
  try {
2089
2100
  if (op.type === "put" && op.content !== null) {
2090
- await this.client.putFile(op.contributorId, op.serverPath, op.content);
2101
+ await this.client.putFile(op.username, op.serverPath, op.content);
2091
2102
  } else if (op.type === "delete") {
2092
- await this.client.deleteFile(op.contributorId, op.serverPath);
2103
+ await this.client.deleteFile(op.username, op.serverPath);
2093
2104
  }
2094
2105
  this.items.shift();
2095
2106
  this.backoff = MIN_BACKOFF;
@@ -2117,13 +2128,13 @@ class RetryQueue {
2117
2128
  // src/daemon/syncer.ts
2118
2129
  class Syncer {
2119
2130
  client;
2120
- contributorId;
2131
+ username;
2121
2132
  collections;
2122
2133
  queue;
2123
2134
  log;
2124
2135
  constructor(opts) {
2125
2136
  this.client = opts.client;
2126
- this.contributorId = opts.contributorId;
2137
+ this.username = opts.username;
2127
2138
  this.collections = opts.collections;
2128
2139
  this.log = opts.onLog;
2129
2140
  this.queue = new RetryQueue(opts.client, opts.onLog);
@@ -2149,7 +2160,7 @@ class Syncer {
2149
2160
  let deleted = 0;
2150
2161
  this.log(`Syncing '${collection.name}' (${collection.path})...`);
2151
2162
  try {
2152
- const { files: serverFiles } = await this.client.listFiles(this.contributorId, collection.name + "/");
2163
+ const { files: serverFiles } = await this.client.listFiles(this.username, collection.name + "/");
2153
2164
  const serverMap = new Map;
2154
2165
  for (const f of serverFiles) {
2155
2166
  serverMap.set(f.path, f.modifiedAt);
@@ -2171,12 +2182,12 @@ class Syncer {
2171
2182
  }
2172
2183
  const content = await readFile(localFile.path, "utf-8");
2173
2184
  try {
2174
- await this.client.putFile(this.contributorId, serverPath, content);
2185
+ await this.client.putFile(this.username, serverPath, content);
2175
2186
  uploaded++;
2176
2187
  } catch {
2177
2188
  this.queue.enqueue({
2178
2189
  type: "put",
2179
- contributorId: this.contributorId,
2190
+ username: this.username,
2180
2191
  serverPath,
2181
2192
  content,
2182
2193
  queuedAt: new Date().toISOString()
@@ -2187,12 +2198,12 @@ class Syncer {
2187
2198
  if (localServerPaths.has(f.path))
2188
2199
  continue;
2189
2200
  try {
2190
- await this.client.deleteFile(this.contributorId, f.path);
2201
+ await this.client.deleteFile(this.username, f.path);
2191
2202
  deleted++;
2192
2203
  } catch {
2193
2204
  this.queue.enqueue({
2194
2205
  type: "delete",
2195
- contributorId: this.contributorId,
2206
+ username: this.username,
2196
2207
  serverPath: f.path,
2197
2208
  content: null,
2198
2209
  queuedAt: new Date().toISOString()
@@ -2210,15 +2221,15 @@ class Syncer {
2210
2221
  let queued = 0;
2211
2222
  this.log(`Removing '${collection.name}' files from server...`);
2212
2223
  try {
2213
- const { files: serverFiles } = await this.client.listFiles(this.contributorId, collection.name + "/");
2224
+ const { files: serverFiles } = await this.client.listFiles(this.username, collection.name + "/");
2214
2225
  for (const f of serverFiles) {
2215
2226
  try {
2216
- await this.client.deleteFile(this.contributorId, f.path);
2227
+ await this.client.deleteFile(this.username, f.path);
2217
2228
  deleted++;
2218
2229
  } catch {
2219
2230
  this.queue.enqueue({
2220
2231
  type: "delete",
2221
- contributorId: this.contributorId,
2232
+ username: this.username,
2222
2233
  serverPath: f.path,
2223
2234
  content: null,
2224
2235
  queuedAt: new Date().toISOString()
@@ -2238,7 +2249,7 @@ class Syncer {
2238
2249
  this.log(`PUT ${event.serverPath} (${content.length} bytes)`);
2239
2250
  this.queue.enqueue({
2240
2251
  type: "put",
2241
- contributorId: this.contributorId,
2252
+ username: this.username,
2242
2253
  serverPath: event.serverPath,
2243
2254
  content,
2244
2255
  queuedAt: new Date().toISOString()
@@ -2247,7 +2258,7 @@ class Syncer {
2247
2258
  this.log(`DELETE ${event.serverPath}`);
2248
2259
  this.queue.enqueue({
2249
2260
  type: "delete",
2250
- contributorId: this.contributorId,
2261
+ username: this.username,
2251
2262
  serverPath: event.serverPath,
2252
2263
  content: null,
2253
2264
  queuedAt: new Date().toISOString()
@@ -2661,7 +2672,7 @@ async function startForeground() {
2661
2672
  maybeLogOverlapWarning(removedOverlappingCollections);
2662
2673
  log("Seedvault daemon starting...");
2663
2674
  log(` Server: ${config.server}`);
2664
- log(` Contributor: ${config.contributorId}`);
2675
+ log(` Contributor: ${config.username}`);
2665
2676
  if (config.collections.length === 0) {
2666
2677
  log(" Collections: none");
2667
2678
  log(" Waiting for collections to be added...");
@@ -2671,7 +2682,7 @@ async function startForeground() {
2671
2682
  writeFileSync2(getPidPath(), String(process.pid));
2672
2683
  const syncer = new Syncer({
2673
2684
  client,
2674
- contributorId: config.contributorId,
2685
+ username: config.username,
2675
2686
  collections: config.collections,
2676
2687
  onLog: log
2677
2688
  });
@@ -2801,7 +2812,7 @@ async function status() {
2801
2812
  console.log(`Seedvault Status
2802
2813
  `);
2803
2814
  console.log(` Server: ${config.server}`);
2804
- console.log(` Contributor: ${config.contributorId}`);
2815
+ console.log(` Contributor: ${config.username}`);
2805
2816
  try {
2806
2817
  const platform = detectPlatform();
2807
2818
  const serviceName = platform === "macos" ? "launchd" : platform === "linux" ? "systemd" : "Task Scheduler";
@@ -2839,7 +2850,7 @@ async function ls(args) {
2839
2850
  const config = loadConfig();
2840
2851
  const client = createClient(config.server, config.token);
2841
2852
  const prefix = args[0] || undefined;
2842
- const { files } = await client.listFiles(config.contributorId, prefix);
2853
+ const { files } = await client.listFiles(config.username, prefix);
2843
2854
  if (files.length === 0) {
2844
2855
  console.log(prefix ? `No files matching '${prefix}'.` : "No files in your contributor.");
2845
2856
  return;
@@ -2871,7 +2882,7 @@ async function cat(args) {
2871
2882
  const config = loadConfig();
2872
2883
  const client = createClient(config.server, config.token);
2873
2884
  try {
2874
- const content = await client.getFile(config.contributorId, filePath);
2885
+ const content = await client.getFile(config.username, filePath);
2875
2886
  process.stdout.write(content);
2876
2887
  } catch (e) {
2877
2888
  if (e instanceof ApiError && e.status === 404) {
@@ -2894,9 +2905,8 @@ async function contributors() {
2894
2905
  console.log(`Contributors:
2895
2906
  `);
2896
2907
  for (const contributor of contributors2) {
2897
- const you = contributor.id === config.contributorId ? " (you)" : "";
2898
- console.log(` ${contributor.name}${you}`);
2899
- console.log(` ID: ${contributor.id}`);
2908
+ const you = contributor.username === config.username ? " (you)" : "";
2909
+ console.log(` ${contributor.username}${you}`);
2900
2910
  console.log(` Created: ${new Date(contributor.createdAt).toLocaleString()}`);
2901
2911
  console.log();
2902
2912
  }
@@ -2930,8 +2940,8 @@ Usage: sv <command> [options]
2930
2940
 
2931
2941
  Setup:
2932
2942
  init Interactive first-time setup
2933
- init --server URL --token T Non-interactive (existing token)
2934
- init --server URL --name N Non-interactive (signup)
2943
+ init --server URL --token T --username U Non-interactive (existing token)
2944
+ init --server URL --name N Non-interactive (signup)
2935
2945
 
2936
2946
  Collections:
2937
2947
  add <path> [--name N] Add a collection path
@@ -2959,7 +2969,9 @@ async function main() {
2959
2969
  return;
2960
2970
  }
2961
2971
  if (cmd === "--version" || cmd === "-v") {
2962
- console.log("0.2.0");
2972
+ const pkgPath = resolve6(import.meta.dirname, "..", "package.json");
2973
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
2974
+ console.log(pkg.version);
2963
2975
  return;
2964
2976
  }
2965
2977
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedvault/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sv": "bin/sv.mjs"