@seedvault/cli 0.4.1 → 0.4.3

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 +113 -23
  2. package/package.json +1 -1
package/dist/sv.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // @bun
3
3
 
4
4
  // src/index.ts
5
- import { readFileSync as readFileSync2 } from "fs";
6
- import { resolve as resolve6 } from "path";
5
+ import { readFileSync as readFileSync3 } from "fs";
6
+ import { resolve as resolve7 } from "path";
7
7
 
8
8
  // src/commands/init.ts
9
9
  import * as readline from "readline/promises";
@@ -2038,12 +2038,26 @@ function watch(paths, options = {}) {
2038
2038
  import { relative as relative4 } from "path";
2039
2039
  function createWatcher(collections2, onEvent) {
2040
2040
  const paths = collections2.map((f) => f.path);
2041
+ const shouldIgnore = (filePath) => {
2042
+ for (const col of collections2) {
2043
+ if (filePath.startsWith(col.path + "/") || filePath === col.path) {
2044
+ const relPath = filePath.slice(col.path.length + 1);
2045
+ const segments = relPath.split("/");
2046
+ for (const seg of segments) {
2047
+ if (seg.startsWith("."))
2048
+ return true;
2049
+ if (seg === "node_modules")
2050
+ return true;
2051
+ if (seg.includes(".tmp."))
2052
+ return true;
2053
+ }
2054
+ return false;
2055
+ }
2056
+ }
2057
+ return false;
2058
+ };
2041
2059
  const watcher = watch(paths, {
2042
- ignored: [
2043
- /(^|[/\\])\./,
2044
- "**/node_modules/**",
2045
- "**/*.tmp.*"
2046
- ],
2060
+ ignored: shouldIgnore,
2047
2061
  persistent: true,
2048
2062
  ignoreInitial: true,
2049
2063
  awaitWriteFinish: {
@@ -2190,8 +2204,37 @@ class Syncer {
2190
2204
  skipped += result.skipped;
2191
2205
  deleted += result.deleted;
2192
2206
  }
2207
+ deleted += await this.purgeOrphans();
2193
2208
  return { uploaded, skipped, deleted };
2194
2209
  }
2210
+ async purgeOrphans() {
2211
+ let deleted = 0;
2212
+ const { files: allServerFiles } = await this.client.listFiles(this.username);
2213
+ const collectionNames = new Set(this.collections.map((c) => c.name));
2214
+ const orphans = allServerFiles.filter((f) => {
2215
+ const prefix = f.path.split("/")[0];
2216
+ return !collectionNames.has(prefix);
2217
+ });
2218
+ if (orphans.length === 0)
2219
+ return 0;
2220
+ this.log(`Purging ${orphans.length} orphaned file(s) from removed collections...`);
2221
+ await pooled(orphans, SYNC_CONCURRENCY, async (f) => {
2222
+ try {
2223
+ await this.client.deleteFile(this.username, f.path);
2224
+ deleted++;
2225
+ } catch {
2226
+ this.queue.enqueue({
2227
+ type: "delete",
2228
+ username: this.username,
2229
+ serverPath: f.path,
2230
+ content: null,
2231
+ queuedAt: new Date().toISOString()
2232
+ });
2233
+ }
2234
+ });
2235
+ this.log(` Purged ${deleted} orphaned file(s)`);
2236
+ return deleted;
2237
+ }
2195
2238
  async syncCollection(collection) {
2196
2239
  let uploaded = 0;
2197
2240
  let skipped = 0;
@@ -2737,15 +2780,13 @@ async function startForeground() {
2737
2780
  collections: config.collections,
2738
2781
  onLog: log
2739
2782
  });
2740
- if (config.collections.length > 0) {
2741
- log("Running initial sync...");
2742
- try {
2743
- const { uploaded, skipped, deleted } = await syncer.initialSync();
2744
- log(`Initial sync complete: ${uploaded} uploaded, ${skipped} skipped, ${deleted} deleted`);
2745
- } catch (e) {
2746
- log(`Initial sync failed: ${e.message}`);
2747
- log("Will continue watching for changes...");
2748
- }
2783
+ log("Running initial sync...");
2784
+ try {
2785
+ const { uploaded, skipped, deleted } = await syncer.initialSync();
2786
+ log(`Initial sync complete: ${uploaded} uploaded, ${skipped} skipped, ${deleted} deleted`);
2787
+ } catch (e) {
2788
+ log(`Initial sync failed: ${e.message}`);
2789
+ log("Will continue watching for changes...");
2749
2790
  }
2750
2791
  let watcher = null;
2751
2792
  const rebuildWatcher = async (collections2) => {
@@ -2950,6 +2991,21 @@ async function cat(args) {
2950
2991
  process.stdout.write(output);
2951
2992
  }
2952
2993
 
2994
+ // src/commands/sh.ts
2995
+ async function sh(args) {
2996
+ if (args.length === 0) {
2997
+ console.error("Usage: sv sh <command>");
2998
+ console.error('Example: sv sh "ls -la yiliu/"');
2999
+ console.error('Example: sv sh "grep -r pattern ."');
3000
+ process.exit(1);
3001
+ }
3002
+ const cmd = args.join(" ");
3003
+ const config = loadConfig();
3004
+ const client = createClient(config.server, config.token);
3005
+ const output = await client.sh(cmd);
3006
+ process.stdout.write(output);
3007
+ }
3008
+
2953
3009
  // src/commands/contributors.ts
2954
3010
  async function contributors() {
2955
3011
  const config = loadConfig();
@@ -2989,6 +3045,31 @@ Share this with the person you want to invite.`);
2989
3045
  }
2990
3046
  }
2991
3047
 
3048
+ // src/commands/update.ts
3049
+ import { readFileSync as readFileSync2 } from "fs";
3050
+ import { resolve as resolve6 } from "path";
3051
+ var {$ } = globalThis.Bun;
3052
+ var INSTALL_SCRIPT_URL = "https://raw.githubusercontent.com/collaborator-ai/seedvault/main/install-cli.sh";
3053
+ async function upgrade() {
3054
+ const pkgPath = resolve6(import.meta.dirname, "..", "..", "package.json");
3055
+ let currentVersion = "unknown";
3056
+ try {
3057
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
3058
+ currentVersion = pkg.version;
3059
+ } catch {}
3060
+ console.log(`Current version: ${currentVersion}`);
3061
+ console.log(`Fetching latest from: ${INSTALL_SCRIPT_URL}
3062
+ `);
3063
+ const result = await $`curl -fsSL ${INSTALL_SCRIPT_URL} | bash -s -- --no-onboard`.quiet().nothrow();
3064
+ if (result.exitCode !== 0) {
3065
+ console.error("Upgrade failed:");
3066
+ console.error(result.stderr.toString());
3067
+ process.exit(1);
3068
+ }
3069
+ console.log(result.stdout.toString());
3070
+ console.log("Upgrade complete. Run 'sv --version' to verify.");
3071
+ }
3072
+
2992
3073
  // src/index.ts
2993
3074
  var USAGE = `
2994
3075
  Seedvault CLI
@@ -2996,9 +3077,10 @@ Seedvault CLI
2996
3077
  Usage: sv <command> [options]
2997
3078
 
2998
3079
  Setup:
2999
- init Interactive first-time setup
3000
- init --server URL --token T --username U Non-interactive (existing token)
3001
- init --server URL --name N Non-interactive (signup)
3080
+ init Interactive first-time setup
3081
+ init --server URL --token T Non-interactive (existing token)
3082
+ init --server URL --name N [--invite CODE] Non-interactive (signup)
3083
+ init --force Overwrite existing config
3002
3084
 
3003
3085
  Collections:
3004
3086
  add <path> [--name N] Add a collection path
@@ -3012,12 +3094,16 @@ Daemon:
3012
3094
  status Show sync status
3013
3095
 
3014
3096
  Files:
3015
- ls [prefix] List files in your contributor
3016
- cat <path> Read a file from the server
3097
+ sh <command> Run a shell command on the vault (ls, cat, grep, etc.)
3098
+ ls [args...] Shorthand for: sv sh "ls [args...]"
3099
+ cat <path> Shorthand for: sv sh "cat <path>"
3017
3100
 
3018
3101
  Vault:
3019
3102
  contributors List all contributors
3020
3103
  invite Generate an invite code (operator only)
3104
+
3105
+ Maintenance:
3106
+ update Update CLI to latest version
3021
3107
  `.trim();
3022
3108
  async function main() {
3023
3109
  const [cmd, ...args] = process.argv.slice(2);
@@ -3026,8 +3112,8 @@ async function main() {
3026
3112
  return;
3027
3113
  }
3028
3114
  if (cmd === "--version" || cmd === "-v") {
3029
- const pkgPath = resolve6(import.meta.dirname, "..", "package.json");
3030
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
3115
+ const pkgPath = resolve7(import.meta.dirname, "..", "package.json");
3116
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
3031
3117
  console.log(pkg.version);
3032
3118
  return;
3033
3119
  }
@@ -3047,6 +3133,8 @@ async function main() {
3047
3133
  return await stop();
3048
3134
  case "status":
3049
3135
  return await status();
3136
+ case "sh":
3137
+ return await sh(args);
3050
3138
  case "ls":
3051
3139
  return await ls(args);
3052
3140
  case "cat":
@@ -3055,6 +3143,8 @@ async function main() {
3055
3143
  return await contributors();
3056
3144
  case "invite":
3057
3145
  return await invite();
3146
+ case "update":
3147
+ return await upgrade();
3058
3148
  default:
3059
3149
  console.error(`Unknown command: ${cmd}
3060
3150
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedvault/cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sv": "bin/sv.mjs"