bavitutor 0.1.1 → 0.1.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.
package/README.md CHANGED
@@ -16,7 +16,14 @@ npx bavitutor
16
16
  ```
17
17
 
18
18
  That's all: it downloads BaviTutor, serves it on <http://localhost:4100> and
19
- opens your browser. Data stays on your machine in `~/.bavitutor`.
19
+ opens your browser. Data stays on your machine in `~/.bavitutor`. If 4100 is
20
+ taken it moves to the next free port, and if BaviTutor is already running it
21
+ opens that one.
22
+
23
+ It always runs the newest version: on start it asks npm, and if a newer one is
24
+ out it starts that one instead, through npx or vpx like you did. (vpx inside a
25
+ pnpm project would otherwise reuse its download for a day.) `--no-update` skips
26
+ this.
20
27
 
21
28
  Requirements: Node 24+, [Vite+](https://viteplus.dev) (`vp`), and a logged-in
22
29
  [Claude Code](https://claude.com/claude-code) install (the Claude Agent SDK
@@ -8,7 +8,7 @@ import { createServer } from "node:http";
8
8
  import { connect, createServer as createServer$1 } from "node:net";
9
9
  import * as NodeCrypto from "node:crypto";
10
10
  import * as NFS from "node:fs";
11
- import { existsSync, mkdirSync, readFileSync } from "node:fs";
11
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
12
12
  import * as OS from "node:os";
13
13
  import { homedir, tmpdir } from "node:os";
14
14
  import * as Path from "node:path";
@@ -46583,7 +46583,8 @@ const codeByLiteral = {
46583
46583
  const fromLiteral = (literal) => codeByLiteral[literal];
46584
46584
  //#endregion
46585
46585
  //#region package.json
46586
- var version$1 = "0.1.1";
46586
+ var name = "bavitutor";
46587
+ var version$1 = "0.1.3";
46587
46588
  /**
46588
46589
  * Default Server-Sent Events encoder.
46589
46590
  *
@@ -61758,6 +61759,7 @@ Options:
61758
61759
  --host <host> interface to listen on (default: ${DEFAULT_HOST}, this machine only)
61759
61760
  --data-dir <dir> database and settings (default: ~/.bavitutor)
61760
61761
  --no-open don't open the browser
61762
+ --no-update don't switch to a newer version from npm
61761
61763
  -v, --version print the version
61762
61764
  -h, --help print this help
61763
61765
 
@@ -61780,6 +61782,10 @@ const parseOptions = () => {
61780
61782
  type: "boolean",
61781
61783
  default: true
61782
61784
  },
61785
+ update: {
61786
+ type: "boolean",
61787
+ default: true
61788
+ },
61783
61789
  version: {
61784
61790
  type: "boolean",
61785
61791
  short: "v"
@@ -61926,6 +61932,88 @@ const choosePort = async (requested, isExplicit, host) => {
61926
61932
  }
61927
61933
  return fail([`Ports ${requested}-${requested + SPARE_PORTS} are all in use.`, "Pick a free one: bavitutor --port <port>"]);
61928
61934
  };
61935
+ /** The version npm calls latest, or undefined when npm doesn't answer quickly. */
61936
+ const latestVersion = async () => {
61937
+ if (process.env["NO_UPDATE_NOTIFIER"]) return;
61938
+ try {
61939
+ const response = await fetch(`https://registry.npmjs.org/${name}/latest`, { signal: AbortSignal.timeout(2e3) });
61940
+ const body = await response.json();
61941
+ return response.ok && body !== null && typeof body === "object" && "version" in body && typeof body.version === "string" ? body.version : void 0;
61942
+ } catch {
61943
+ return;
61944
+ }
61945
+ };
61946
+ const isNewer = (candidate, current) => {
61947
+ const parts = (version) => (version.split("-")[0] ?? "").split(".").map(Number);
61948
+ const [next, now] = [parts(candidate), parts(current)];
61949
+ const difference = [
61950
+ 0,
61951
+ 1,
61952
+ 2
61953
+ ].map((i) => (next[i] ?? 0) - (now[i] ?? 0)).find((delta) => delta !== 0);
61954
+ return difference !== void 0 && difference > 0;
61955
+ };
61956
+ /**
61957
+ * How to start another version the way this one was started: npx, and vpx
61958
+ * outside a pnpm project, run `npm exec`; vpx inside one runs `pnpm dlx`,
61959
+ * which reuses what it downloaded for a day. An exact version is always
61960
+ * fetched. Undefined for a global install or a checkout's own build.
61961
+ */
61962
+ const relaunchCommand = (version) => {
61963
+ const runner = process.env["npm_execpath"];
61964
+ const spec = `${name}@${version}`;
61965
+ const args = process.argv.slice(2);
61966
+ const runnerArgs = process.env["npm_command"] === "exec" ? [
61967
+ "exec",
61968
+ "--yes",
61969
+ `--package=${spec}`,
61970
+ "--",
61971
+ name,
61972
+ ...args
61973
+ ] : process.env["npm_command"] === "dlx" ? [
61974
+ "dlx",
61975
+ spec,
61976
+ ...args
61977
+ ] : void 0;
61978
+ if (!runner || runnerArgs === void 0) return;
61979
+ return /\.[cm]?js$/.test(runner) ? [process.execPath, [runner, ...runnerArgs]] : [runner, runnerArgs];
61980
+ };
61981
+ /** Set for the newer version, which deletes the file once its code runs. */
61982
+ const RELAUNCH_MARKER = "BAVI_RELAUNCH_MARKER";
61983
+ /**
61984
+ * Runs the newer version in this terminal and resolves with its exit code,
61985
+ * or undefined if it never started (say npm can't serve it yet).
61986
+ */
61987
+ const runNewer = (command, args) => new Promise((done) => {
61988
+ const marker = join(tmpdir(), `bavitutor-relaunch-${process.pid}`);
61989
+ writeFileSync(marker, "");
61990
+ const waitForChild = () => {};
61991
+ process.on("SIGINT", waitForChild);
61992
+ const finish = (code) => {
61993
+ process.off("SIGINT", waitForChild);
61994
+ const hasStarted = !existsSync(marker);
61995
+ rmSync(marker, { force: true });
61996
+ done(hasStarted ? code : void 0);
61997
+ };
61998
+ const child = spawn(command, args, {
61999
+ stdio: "inherit",
62000
+ env: {
62001
+ ...process.env,
62002
+ [RELAUNCH_MARKER]: marker
62003
+ }
62004
+ });
62005
+ child.once("error", () => finish(void 0));
62006
+ child.once("exit", (code) => finish(code ?? 0));
62007
+ });
62008
+ /** Where starting the newest version wasn't possible, says how to. */
62009
+ const sayIfOutdated = (latest) => {
62010
+ if (latest !== void 0 && isNewer(latest, version$1)) {
62011
+ say(`BaviTutor ${latest} is out (this is ${version$1}). To update,`);
62012
+ say(`stop this one and run: npx bavitutor@${latest}`);
62013
+ say(` or: vpx bavitutor@${latest}`);
62014
+ say();
62015
+ }
62016
+ };
61929
62017
  const openBrowser = (url) => {
61930
62018
  const [command, args] = process.platform === "win32" ? ["cmd.exe", [
61931
62019
  "/d",
@@ -61957,6 +62045,23 @@ const main = async () => {
61957
62045
  say(version$1);
61958
62046
  return;
61959
62047
  }
62048
+ const marker = process.env[RELAUNCH_MARKER];
62049
+ if (marker !== void 0) {
62050
+ rmSync(marker, { force: true });
62051
+ delete process.env[RELAUNCH_MARKER];
62052
+ }
62053
+ const latest = options.update && marker === void 0 ? await latestVersion() : void 0;
62054
+ const relaunch = latest !== void 0 && isNewer(latest, version$1) ? relaunchCommand(latest) : void 0;
62055
+ if (latest !== void 0 && relaunch !== void 0) {
62056
+ say(`BaviTutor ${latest} is out, starting it instead of ${version$1}`);
62057
+ const code = await runNewer(...relaunch);
62058
+ if (code !== void 0) {
62059
+ process.exitCode = code;
62060
+ return;
62061
+ }
62062
+ say(` couldn't start ${latest}, so staying on ${version$1}`);
62063
+ say();
62064
+ }
61960
62065
  const dataDir = resolve(expandHome(options["data-dir"] ?? join(homedir(), ".bavitutor")));
61961
62066
  mkdirSync(dataDir, { recursive: true });
61962
62067
  const envFile = join(dataDir, ".env");
@@ -61997,7 +62102,9 @@ const main = async () => {
61997
62102
  if (choice.isRunning) {
61998
62103
  say();
61999
62104
  say(`BaviTutor is already running at ${urlFor(choice.port)}`);
62105
+ say();
62000
62106
  if (options.open) openBrowser(urlFor(choice.port));
62107
+ sayIfOutdated(latest);
62001
62108
  return;
62002
62109
  }
62003
62110
  const port = choice.port;
@@ -62009,6 +62116,7 @@ const main = async () => {
62009
62116
  say(`BaviTutor is running at ${url} (Ctrl+C to stop)`);
62010
62117
  say();
62011
62118
  if (options.open) openBrowser(url);
62119
+ sayIfOutdated(latest);
62012
62120
  })).pipe(provide(makeServer({
62013
62121
  port,
62014
62122
  host,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bavitutor",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Describe what you want to learn; BaviTutor researches it, maps it and teaches it.",
5
5
  "keywords": [
6
6
  "claude",