@dropthis/cli 0.12.0 → 0.13.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/README.md CHANGED
@@ -180,6 +180,7 @@ dropthis update-settings <drop-id> # Change title/visibility/password/
180
180
  dropthis pull <id|url|slug> [-o <dir>] # Download a drop's files to a local directory
181
181
  dropthis get <id|url|slug> # Show drop details
182
182
  dropthis list # List your drops
183
+ dropthis list --domain reports.example.com # Only drops on a custom domain
183
184
  dropthis delete <id|url|slug> # Delete a drop (--yes to confirm)
184
185
 
185
186
  dropthis deployments # View deployment history
@@ -197,6 +198,7 @@ dropthis api-keys create # Create an API key
197
198
  dropthis api-keys list # List API keys
198
199
  dropthis api-keys delete <key-id> # Delete an API key (--yes to confirm)
199
200
 
201
+ dropthis upgrade # Update the CLI to the latest version
200
202
  dropthis doctor # CLI diagnostics
201
203
  dropthis commands # Machine-readable command metadata
202
204
  ```
@@ -211,6 +213,7 @@ The CLI is designed for non-interactive use. In non-TTY environments (pipes, CI,
211
213
  export DROPTHIS_API_KEY=sk_live_... # API key (same as --api-key)
212
214
  export DROPTHIS_API_URL=https://... # Override API base URL (same as --api-url)
213
215
  export DROPTHIS_NON_INTERACTIVE=1 # Disable interactive prompts (same as --no-interactive)
216
+ export DROPTHIS_NO_UPDATE_NOTIFIER=1 # Disable the startup update notice (also auto-disabled in CI, non-TTY, --json/--quiet)
214
217
  ```
215
218
 
216
219
  ```bash
package/dist/cli.cjs CHANGED
@@ -224,6 +224,9 @@ function cliNextAction(error2) {
224
224
  if (error2.code === "revision_conflict") {
225
225
  return "Re-read the drop with dropthis get <drop-id>, merge your changes, and retry with --if-revision set to the current revision.";
226
226
  }
227
+ if (error2.code === "publish_conflict") {
228
+ return "The drop changed state mid-publish (likely deleted). Run dropthis publish again to create a fresh drop.";
229
+ }
227
230
  if (error2.statusCode === 404 || error2.code === "not_found") {
228
231
  return "Check the drop id or slug and retry; list your drops with dropthis list.";
229
232
  }
@@ -672,7 +675,10 @@ var COMMAND_ANNOTATIONS = {
672
675
  },
673
676
  list: {
674
677
  auth: "required",
675
- examples: ["dropthis list --json"]
678
+ examples: [
679
+ "dropthis list --json",
680
+ "dropthis list --domain reports.example.com --json"
681
+ ]
676
682
  },
677
683
  delete: {
678
684
  auth: "required",
@@ -742,6 +748,7 @@ var COMMAND_ANNOTATIONS = {
742
748
  examples: ["dropthis account delete --yes --json"]
743
749
  },
744
750
  doctor: { examples: ["dropthis doctor --json"] },
751
+ upgrade: { examples: ["dropthis upgrade", "dropthis upgrade --json"] },
745
752
  commands: { examples: ["dropthis commands --json"] }
746
753
  };
747
754
  function describeArgs(cmd) {
@@ -873,13 +880,13 @@ async function runDoctor(_input, deps) {
873
880
  const authSource = credential?.source ?? "missing";
874
881
  const storageBackend = stored?.storage ?? "none";
875
882
  const pairs = [
876
- ["Version", "0.12.0"],
883
+ ["Version", "0.13.0"],
877
884
  ["Auth", authSource],
878
885
  ["Storage", storageBackend]
879
886
  ];
880
887
  writeKv(deps, pairs, {
881
888
  ok: true,
882
- version: "0.12.0",
889
+ version: "0.13.0",
883
890
  auth: { source: authSource },
884
891
  storage: { backend: storageBackend }
885
892
  });
@@ -1265,7 +1272,8 @@ async function runDropsList(input, deps) {
1265
1272
  }
1266
1273
  const params = {
1267
1274
  ...input.limit !== void 0 ? { limit: input.limit } : {},
1268
- ...input.cursor ? { cursor: input.cursor } : {}
1275
+ ...input.cursor ? { cursor: input.cursor } : {},
1276
+ ...input.domain ? { domain: input.domain } : {}
1269
1277
  };
1270
1278
  const result = await deps.client.drops.list(params);
1271
1279
  if (result.error) return writeApiError(deps, result.error);
@@ -2200,6 +2208,109 @@ function assertDropId2(target) {
2200
2208
  }
2201
2209
  }
2202
2210
 
2211
+ // src/commands/upgrade.ts
2212
+ var import_node_child_process = require("child_process");
2213
+ var import_node_fs = require("fs");
2214
+ var import_node_util = require("util");
2215
+
2216
+ // src/semver.ts
2217
+ function compareSemver(a, b) {
2218
+ const parse = (v) => (v.split("-")[0] ?? v).split(".").map(Number);
2219
+ const pa = parse(a);
2220
+ const pb = parse(b);
2221
+ for (let i = 0; i < 3; i++) {
2222
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
2223
+ if (d !== 0) return d < 0 ? -1 : 1;
2224
+ }
2225
+ return 0;
2226
+ }
2227
+
2228
+ // src/commands/upgrade.ts
2229
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
2230
+ var REGISTRY_LATEST = "https://registry.npmjs.org/@dropthis%2fcli/latest";
2231
+ async function fetchLatestFromRegistry() {
2232
+ try {
2233
+ const res = await fetch(REGISTRY_LATEST, {
2234
+ signal: AbortSignal.timeout(1e4)
2235
+ });
2236
+ if (!res.ok) return null;
2237
+ const body = await res.json();
2238
+ return typeof body.version === "string" ? body.version : null;
2239
+ } catch {
2240
+ return null;
2241
+ }
2242
+ }
2243
+ async function detectNpmGlobalInstall() {
2244
+ try {
2245
+ const argv1 = process.argv[1];
2246
+ if (!argv1) return false;
2247
+ const entry = (0, import_node_fs.realpathSync)(argv1);
2248
+ const { stdout } = await execFileAsync("npm", ["root", "-g"]);
2249
+ const root = (0, import_node_fs.realpathSync)(stdout.trim());
2250
+ return entry.startsWith(root);
2251
+ } catch {
2252
+ return false;
2253
+ }
2254
+ }
2255
+ async function npmInstallLatest() {
2256
+ try {
2257
+ await execFileAsync("npm", ["install", "-g", "@dropthis/cli@latest"]);
2258
+ return { ok: true };
2259
+ } catch (err) {
2260
+ return {
2261
+ ok: false,
2262
+ message: err instanceof Error ? err.message : String(err)
2263
+ };
2264
+ }
2265
+ }
2266
+ async function runUpgrade(_input, deps) {
2267
+ const fetchLatest = deps.fetchLatest ?? fetchLatestFromRegistry;
2268
+ const isNpmGlobal = deps.isNpmGlobalInstall ?? detectNpmGlobalInstall;
2269
+ const install = deps.runNpmInstall ?? npmInstallLatest;
2270
+ const latest = await fetchLatest();
2271
+ if (!latest) {
2272
+ return writeError(
2273
+ deps,
2274
+ "upgrade_check_failed",
2275
+ "Could not reach the npm registry to check for updates. Try again, or run: npm install -g @dropthis/cli@latest"
2276
+ );
2277
+ }
2278
+ if (compareSemver(latest, deps.currentVersion) <= 0) {
2279
+ writeHumanOrJson(
2280
+ deps,
2281
+ `Already on latest version (${deps.currentVersion})`,
2282
+ { ok: true, status: "already-latest", version: deps.currentVersion }
2283
+ );
2284
+ return { exitCode: 0 };
2285
+ }
2286
+ if (!await isNpmGlobal()) {
2287
+ return writeError(
2288
+ deps,
2289
+ "unsupported_install",
2290
+ `Update available (${deps.currentVersion} \u2192 ${latest}), but this install was not made with \`npm install -g\`. Upgrade with the installer you used, e.g.: npm install -g @dropthis/cli@latest`
2291
+ );
2292
+ }
2293
+ if (deps.outputMode === "human") {
2294
+ deps.stdout(`Updating ${deps.currentVersion} \u2192 ${latest}...
2295
+ `);
2296
+ }
2297
+ const result = await install();
2298
+ if (!result.ok) {
2299
+ return writeError(
2300
+ deps,
2301
+ "upgrade_failed",
2302
+ `npm install failed${result.message ? `: ${result.message}` : ""}. Try: npm install -g @dropthis/cli@latest`
2303
+ );
2304
+ }
2305
+ writeHumanOrJson(deps, `Updated ${deps.currentVersion} \u2192 ${latest}`, {
2306
+ ok: true,
2307
+ status: "upgraded",
2308
+ from: deps.currentVersion,
2309
+ to: latest
2310
+ });
2311
+ return { exitCode: 0 };
2312
+ }
2313
+
2203
2314
  // src/commands/whoami.ts
2204
2315
  async function runWhoami(_input, deps) {
2205
2316
  const credential = await resolveCredential({
@@ -2455,7 +2566,7 @@ function buildProgram(options = {}) {
2455
2566
  ` ${import_picocolors6.default.cyan("dropthis login")}`,
2456
2567
  ` ${import_picocolors6.default.cyan("dropthis publish ./dist")}`
2457
2568
  ].join("\n")
2458
- ).version("0.12.0").configureHelp({
2569
+ ).version("0.13.0").configureHelp({
2459
2570
  subcommandTerm(cmd) {
2460
2571
  const args = cmd.registeredArguments.map((a) => {
2461
2572
  const name = a.name();
@@ -2695,11 +2806,12 @@ ${lines.map((l) => ` ${l}`).join("\n")}
2695
2806
  const result = await runDropsGet(dropId, commandOptions, deps);
2696
2807
  process.exitCode = result.exitCode;
2697
2808
  });
2698
- program.command("list").description("List your drops").option("--limit <n>", "Page size", parseInteger).option("--cursor <cursor>", "Pagination cursor").option("--json", "Force JSON output").addHelpText(
2809
+ program.command("list").description("List your drops").option("--limit <n>", "Page size", parseInteger).option("--cursor <cursor>", "Pagination cursor").option("--domain <hostname>", "Only drops mounted on this custom domain").option("--json", "Force JSON output").addHelpText(
2699
2810
  "after",
2700
2811
  examplesBlock([
2701
2812
  "dropthis list",
2702
- "dropthis list --json | jq -r '.drops[] | [.id, .url] | @tsv' # recover drop ids"
2813
+ "dropthis list --json | jq -r '.drops[] | [.id, .url] | @tsv' # recover drop ids",
2814
+ "dropthis list --domain reports.example.com --json # drops on a custom domain"
2703
2815
  ])
2704
2816
  ).action(
2705
2817
  async (commandOptions) => {
@@ -2986,6 +3098,19 @@ ${lines.map((l) => ` ${l}`).join("\n")}
2986
3098
  const result = await runDoctor(commandOptions, deps);
2987
3099
  process.exitCode = result.exitCode;
2988
3100
  });
3101
+ program.command("upgrade").description("Update the CLI to the latest version from npm").option("--json", "Force JSON output").action(async (commandOptions) => {
3102
+ const deps = await commandDeps(
3103
+ program,
3104
+ options,
3105
+ store,
3106
+ commandOptions
3107
+ );
3108
+ const result = await runUpgrade(commandOptions, {
3109
+ ...deps,
3110
+ currentVersion: "0.13.0"
3111
+ });
3112
+ process.exitCode = result.exitCode;
3113
+ });
2989
3114
  program.command("commands").description("Print machine-readable command metadata").option("--json", "Force JSON output").action(async (commandOptions) => {
2990
3115
  const deps = await commandDeps(
2991
3116
  program,
@@ -3134,6 +3259,72 @@ async function readStdinBytes(stdin) {
3134
3259
  return Buffer.concat(chunks);
3135
3260
  }
3136
3261
 
3262
+ // src/update-check.ts
3263
+ var import_node_child_process2 = require("child_process");
3264
+ var import_node_fs2 = require("fs");
3265
+ var import_node_os3 = require("os");
3266
+ var import_node_path3 = require("path");
3267
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
3268
+ var REGISTRY_LATEST2 = "https://registry.npmjs.org/@dropthis%2fcli/latest";
3269
+ function updateCachePath(configDir) {
3270
+ return (0, import_node_path3.join)(
3271
+ configDir ?? (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".config", "dropthis"),
3272
+ "update-check.json"
3273
+ );
3274
+ }
3275
+ function readCache(path) {
3276
+ try {
3277
+ const value = JSON.parse((0, import_node_fs2.readFileSync)(path, "utf8"));
3278
+ if (typeof value.lastCheckedAt !== "string" || typeof value.latestVersion !== "string")
3279
+ return null;
3280
+ return {
3281
+ lastCheckedAt: value.lastCheckedAt,
3282
+ latestVersion: value.latestVersion
3283
+ };
3284
+ } catch {
3285
+ return null;
3286
+ }
3287
+ }
3288
+ function shouldShowNotice(input) {
3289
+ if (input.env.CI) return false;
3290
+ if (input.env.DROPTHIS_NO_UPDATE_NOTIFIER) return false;
3291
+ if (!input.stderrIsTTY) return false;
3292
+ if (input.argv.includes("--json") || input.argv.includes("--quiet") || input.argv.includes("-q"))
3293
+ return false;
3294
+ return true;
3295
+ }
3296
+ function noticeFromCache(input) {
3297
+ if (!input.cache) return null;
3298
+ if (compareSemver(input.cache.latestVersion, input.currentVersion) <= 0)
3299
+ return null;
3300
+ return `Update available ${input.currentVersion} \u2192 ${input.cache.latestVersion} \xB7 run \`dropthis upgrade\``;
3301
+ }
3302
+ function shouldRefresh(cache, now) {
3303
+ if (!cache) return true;
3304
+ const last = Date.parse(cache.lastCheckedAt);
3305
+ if (Number.isNaN(last)) return true;
3306
+ return now.getTime() - last >= CHECK_INTERVAL_MS;
3307
+ }
3308
+ function scheduleBackgroundRefresh(cachePath, spawnFn = import_node_child_process2.spawn) {
3309
+ const script = `
3310
+ const [, cachePath] = process.argv;
3311
+ fetch(${JSON.stringify(REGISTRY_LATEST2)}, { signal: AbortSignal.timeout(5000) })
3312
+ .then((r) => (r.ok ? r.json() : Promise.reject(new Error(String(r.status)))))
3313
+ .then((d) => {
3314
+ const fs = require("node:fs");
3315
+ const path = require("node:path");
3316
+ fs.mkdirSync(path.dirname(cachePath), { recursive: true });
3317
+ fs.writeFileSync(cachePath, JSON.stringify({ lastCheckedAt: new Date().toISOString(), latestVersion: String(d.version) }));
3318
+ })
3319
+ .catch(() => {});
3320
+ `;
3321
+ const child = spawnFn(process.execPath, ["-e", script, cachePath], {
3322
+ detached: true,
3323
+ stdio: "ignore"
3324
+ });
3325
+ child.unref();
3326
+ }
3327
+
3137
3328
  // src/cli.ts
3138
3329
  function writeUsageErrorEnvelope(input) {
3139
3330
  if (input.json) {
@@ -3150,7 +3341,29 @@ function isCommanderError(err) {
3150
3341
  function wantsJson(argv) {
3151
3342
  return argv.includes("--json") || argv.includes("--quiet") || argv.includes("-q") || process.env.CI === "true" || process.stdout.isTTY !== true;
3152
3343
  }
3153
- buildProgram({ store: createCredentialStore() }).parseAsync(process.argv).catch((error2) => {
3344
+ var updateCache = readCache(updateCachePath());
3345
+ var showNotice = shouldShowNotice({
3346
+ env: process.env,
3347
+ stderrIsTTY: process.stderr.isTTY === true,
3348
+ argv: process.argv.slice(2)
3349
+ });
3350
+ if (showNotice) {
3351
+ const notice = noticeFromCache({
3352
+ cache: updateCache,
3353
+ currentVersion: "0.13.0"
3354
+ });
3355
+ if (notice) {
3356
+ process.stderr.write(`
3357
+ ${hint(notice)}
3358
+
3359
+ `);
3360
+ }
3361
+ }
3362
+ buildProgram({ store: createCredentialStore() }).parseAsync(process.argv).finally(() => {
3363
+ if (showNotice && shouldRefresh(updateCache, /* @__PURE__ */ new Date())) {
3364
+ scheduleBackgroundRefresh(updateCachePath());
3365
+ }
3366
+ }).catch((error2) => {
3154
3367
  if (isCommanderError(error2)) {
3155
3368
  if (error2.code === "commander.help" || error2.code === "commander.helpDisplayed" || error2.code === "commander.version") {
3156
3369
  process.exitCode = 0;