@withone/cli 1.44.0 → 1.44.2

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 (3) hide show
  1. package/README.md +14 -0
  2. package/dist/index.js +59 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -548,6 +548,20 @@ Settings propagate automatically to all installed agent configs.
548
548
 
549
549
  Auto-sync refuses to resurrect skills if you opted out of skill installation during `one init` — the canonical dir has to already exist.
550
550
 
551
+ ### `one update`
552
+
553
+ Updates the CLI to the latest version (`npm install -g @withone/cli@latest`).
554
+
555
+ The CLI also **auto-updates in the background**: when a newer version has been published for more than 30 minutes, the next `one` command silently kicks off a global install. This is hardened against concurrent runs — a lock file in `~/.one/` ensures at most one install runs at a time (so parallel agent invocations can't collide and wedge npm), and the lock self-heals after 10 minutes if an install ever crashes or hangs.
556
+
557
+ To disable background auto-updates entirely (e.g. on CI or a shared agent host), set:
558
+
559
+ ```bash
560
+ export ONE_NO_AUTO_UPDATE=1
561
+ ```
562
+
563
+ Run `one update` manually whenever you want to upgrade.
564
+
551
565
  ### Project config (`.onerc`)
552
566
 
553
567
  Drop a `.onerc` file in your project root to override global settings per-project. Simple `KEY=VALUE` format; `#` for comments. Read from the current working directory (no parent lookup).
package/dist/index.js CHANGED
@@ -621,14 +621,17 @@ import { fileURLToPath } from "url";
621
621
  // src/commands/update.ts
622
622
  import { createRequire } from "module";
623
623
  import { spawn } from "child_process";
624
- import { readFileSync, writeFileSync, mkdirSync } from "fs";
624
+ import { readFileSync, writeFileSync, mkdirSync, rmSync } from "fs";
625
625
  import { homedir } from "os";
626
626
  import { join } from "path";
627
627
  var require2 = createRequire(import.meta.url);
628
628
  var { version: currentVersion } = require2("../package.json");
629
- var CACHE_PATH = join(homedir(), ".one", "update-check.json");
629
+ var ONE_DIR = join(homedir(), ".one");
630
+ var CACHE_PATH = join(ONE_DIR, "update-check.json");
631
+ var LOCK_PATH = join(ONE_DIR, "auto-update.lock");
630
632
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
631
633
  var AGE_GATE_MS = 30 * 60 * 1e3;
634
+ var LOCK_TTL_MS = 10 * 60 * 1e3;
632
635
  async function fetchLatestVersionInfo() {
633
636
  try {
634
637
  const res = await fetch("https://registry.npmjs.org/@withone/cli");
@@ -725,16 +728,52 @@ function isNewerVersion(latest, current) {
725
728
  if (lMin !== cMin) return lMin > cMin;
726
729
  return lPat > cPat;
727
730
  }
731
+ function isAutoUpdateDisabled() {
732
+ const v = process.env.ONE_NO_AUTO_UPDATE ?? process.env.ONE_DISABLE_AUTO_UPDATE;
733
+ return v === "1" || v === "true";
734
+ }
735
+ function acquireUpdateLock(targetVersion) {
736
+ try {
737
+ mkdirSync(ONE_DIR, { recursive: true });
738
+ } catch {
739
+ }
740
+ try {
741
+ const lock = JSON.parse(readFileSync(LOCK_PATH, "utf8"));
742
+ const startedAt = typeof lock.startedAt === "number" ? lock.startedAt : 0;
743
+ if (Date.now() - startedAt < LOCK_TTL_MS) return false;
744
+ rmSync(LOCK_PATH, { force: true });
745
+ } catch {
746
+ }
747
+ try {
748
+ writeFileSync(
749
+ LOCK_PATH,
750
+ JSON.stringify({ pid: process.pid, startedAt: Date.now(), targetVersion }),
751
+ { flag: "wx" }
752
+ // fail if another invocation created it first
753
+ );
754
+ return true;
755
+ } catch {
756
+ return false;
757
+ }
758
+ }
728
759
  function autoUpdate(targetVersion, publishedAt) {
760
+ if (isAutoUpdateDisabled()) return;
729
761
  if (publishedAt) {
730
762
  const age = Date.now() - new Date(publishedAt).getTime();
731
763
  if (age < AGE_GATE_MS) return;
732
764
  }
765
+ if (!acquireUpdateLock(targetVersion)) return;
733
766
  const child = spawn("npm", ["install", "-g", `@withone/cli@${targetVersion}`], {
734
767
  detached: true,
735
768
  stdio: "ignore",
736
769
  shell: true
737
770
  });
771
+ child.on("error", () => {
772
+ try {
773
+ rmSync(LOCK_PATH, { force: true });
774
+ } catch {
775
+ }
776
+ });
738
777
  child.unref();
739
778
  }
740
779
 
@@ -6273,17 +6312,23 @@ async function syncModel(api, profile, options) {
6273
6312
  if (lock) lock.release();
6274
6313
  const rawMsg = err instanceof Error ? err.message : String(err);
6275
6314
  const shortMsg = truncate(rawMsg, 500);
6315
+ const httpStatus = err instanceof ApiError ? err.status : void 0;
6316
+ const retryAfter = err instanceof ApiError ? err.retryAfterSeconds : void 0;
6276
6317
  if (pagesProcessed > 0) {
6277
6318
  const resumeErr = new Error(
6278
6319
  `Sync interrupted after page ${pagesProcessed} (${totalRecords} records). Run again to resume. Error: ${shortMsg}`
6279
6320
  );
6280
6321
  resumeErr._recordsSynced = totalRecords;
6281
6322
  resumeErr._pagesProcessed = pagesProcessed;
6323
+ resumeErr._httpStatus = httpStatus;
6324
+ resumeErr._retryAfter = retryAfter;
6282
6325
  throw resumeErr;
6283
6326
  }
6284
6327
  const wrapped = new Error(shortMsg);
6285
6328
  wrapped._recordsSynced = totalRecords;
6286
6329
  wrapped._pagesProcessed = pagesProcessed;
6330
+ wrapped._httpStatus = httpStatus;
6331
+ wrapped._retryAfter = retryAfter;
6287
6332
  throw wrapped;
6288
6333
  } finally {
6289
6334
  process.off("SIGINT", onSigint);
@@ -7762,13 +7807,18 @@ async function syncRunCommand(platform, options) {
7762
7807
  results.push(result);
7763
7808
  } catch (err) {
7764
7809
  const errObj = err;
7810
+ const errorContext = {
7811
+ message: err instanceof Error ? err.message : String(err),
7812
+ ...errObj?._httpStatus !== void 0 ? { httpStatus: errObj._httpStatus } : {},
7813
+ ...errObj?._retryAfter !== void 0 ? { retryAfter: errObj._retryAfter } : {}
7814
+ };
7765
7815
  results.push({
7766
7816
  model: profile.model,
7767
7817
  recordsSynced: errObj?._recordsSynced ?? 0,
7768
7818
  pagesProcessed: errObj?._pagesProcessed ?? 0,
7769
7819
  duration: "0s",
7770
7820
  status: "failed",
7771
- error: err instanceof Error ? err.message : String(err)
7821
+ error: errorContext
7772
7822
  });
7773
7823
  }
7774
7824
  }
@@ -7787,8 +7837,11 @@ async function syncRunCommand(platform, options) {
7787
7837
  const archivedColor = sc.archived > sc.active ? pc9.red : pc9.dim;
7788
7838
  console.log(` memory: ${pc9.green(String(sc.active))} active, ${archivedColor(String(sc.archived))} archived`);
7789
7839
  }
7790
- if ("error" in r && r.error) {
7791
- console.log(` ${pc9.red(r.error)}`);
7840
+ if (r.error) {
7841
+ const errParts = [r.error.message];
7842
+ if (r.error.httpStatus) errParts.push(`HTTP ${r.error.httpStatus}`);
7843
+ if (r.error.retryAfter) errParts.push(`retry after ${r.error.retryAfter}s`);
7844
+ console.log(` ${pc9.red(errParts.join(" \u2014 "))}`);
7792
7845
  }
7793
7846
  }
7794
7847
  }
@@ -11121,7 +11174,7 @@ program.command("guide [topic]").description("Full CLI usage guide for agents (t
11121
11174
  program.command("onboard").description("Agent onboarding \u2014 teaches your agent what the One CLI can do").option("--step <number>", "Run a specific onboarding step (1, 2, or 3)").action(async (options) => {
11122
11175
  await onboardCommand(options.step ? parseInt(options.step, 10) : void 0);
11123
11176
  });
11124
- program.command("update").description("Update the One CLI to the latest version").action(async () => {
11177
+ program.command("update").description("Update the One CLI to the latest version (background auto-update can be disabled with ONE_NO_AUTO_UPDATE=1)").action(async () => {
11125
11178
  await updateCommand();
11126
11179
  });
11127
11180
  program.command("whoami").description("Show the user, organization, and project for the current API key").action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.44.0",
3
+ "version": "1.44.2",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [