@raegent/earshot 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.
package/dist/main.js CHANGED
@@ -18381,7 +18381,7 @@ class ShadowGit {
18381
18381
  }
18382
18382
 
18383
18383
  // packages/core/src/version.ts
18384
- var VERSION = "0.2.0";
18384
+ var VERSION = "0.3.0";
18385
18385
 
18386
18386
  // packages/core/src/session/repair.ts
18387
18387
  var REPAIR_TEXT = "No result was recorded for this call: earshot exited before the tool finished. " + "The call may or may not have run, so treat its effect as unknown and check " + "the current state rather than assuming either outcome.";
@@ -18642,7 +18642,16 @@ function parseArgs(argv) {
18642
18642
  positionals.push(arg);
18643
18643
  }
18644
18644
  }
18645
- const known = new Set(["auth", "mcp", "extensions", "config", "models", "acp", "doctor"]);
18645
+ const known = new Set([
18646
+ "auth",
18647
+ "mcp",
18648
+ "extensions",
18649
+ "config",
18650
+ "models",
18651
+ "acp",
18652
+ "doctor",
18653
+ "update"
18654
+ ]);
18646
18655
  const command = positionals[0] !== undefined && known.has(positionals[0]) ? positionals[0] : undefined;
18647
18656
  return { command, flags, positionals: command ? positionals.slice(1) : positionals };
18648
18657
  }
@@ -22654,6 +22663,343 @@ ${models.length} models across ${new Set(models.map((m) => m.providerId)).size}
22654
22663
  return 0;
22655
22664
  }
22656
22665
 
22666
+ // packages/cli/src/commands/update.ts
22667
+ import { spawnSync as spawnSync2 } from "node:child_process";
22668
+ import { createHash as createHash4 } from "node:crypto";
22669
+ import { existsSync as existsSync2, realpathSync } from "node:fs";
22670
+ import { chmod, readdir as readdir8, rename as rename2, rm as rm2, writeFile as writeFile10 } from "node:fs/promises";
22671
+ import { basename as basename2, dirname as dirname9, join as join19, sep as sep5 } from "node:path";
22672
+ import { createInterface } from "node:readline/promises";
22673
+ import { fileURLToPath } from "node:url";
22674
+ var PACKAGE = "@raegent/earshot";
22675
+ var REPO = "rishabhguptajs/earshot";
22676
+ var REGISTRY = `https://registry.npmjs.org/${PACKAGE.replace("/", "%2f")}/latest`;
22677
+ var RELEASE = `https://api.github.com/repos/${REPO}/releases/latest`;
22678
+ var BUNFS_MARKERS = ["/$bunfs/", "\\$bunfs\\", "/~BUN/", "\\~BUN\\"];
22679
+ var ASSETS = {
22680
+ "darwin-arm64": "earshot-darwin-arm64",
22681
+ "darwin-x64": "earshot-darwin-x64",
22682
+ "linux-x64": "earshot-linux-x64",
22683
+ "linux-arm64": "earshot-linux-arm64",
22684
+ "win32-x64": "earshot-windows-x64.exe"
22685
+ };
22686
+ function modulePath(moduleUrl) {
22687
+ try {
22688
+ return fileURLToPath(moduleUrl);
22689
+ } catch {
22690
+ return moduleUrl;
22691
+ }
22692
+ }
22693
+ function managerOf(dir) {
22694
+ const lower = dir.toLowerCase().replaceAll("\\", "/");
22695
+ if (lower.includes("/.bun/install/global"))
22696
+ return "bun";
22697
+ if (lower.includes("/.volta/"))
22698
+ return "volta";
22699
+ if (lower.includes("/pnpm/global") || lower.includes("/.pnpm/"))
22700
+ return "pnpm";
22701
+ if (lower.includes("/.yarn/") || lower.includes("/yarn/global"))
22702
+ return "yarn";
22703
+ return "npm";
22704
+ }
22705
+ function detectInstall(options) {
22706
+ const { execPath, moduleUrl, bunVersion, platform: platform7, arch } = options;
22707
+ const realpath = options.realpath ?? ((path) => path);
22708
+ const file = modulePath(moduleUrl);
22709
+ if (BUNFS_MARKERS.some((marker3) => file.includes(marker3)) && bunVersion !== undefined) {
22710
+ const asset = ASSETS[`${platform7}-${arch}`];
22711
+ const target = realpath(execPath);
22712
+ if (asset === undefined) {
22713
+ return {
22714
+ kind: "binary",
22715
+ path: target,
22716
+ reason: `no release asset is built for ${platform7}-${arch}`
22717
+ };
22718
+ }
22719
+ return { kind: "binary", path: target, asset };
22720
+ }
22721
+ const marker2 = `${sep5}node_modules${sep5}${PACKAGE.split("/").join(sep5)}${sep5}`;
22722
+ const index = `${file}${sep5}`.indexOf(marker2);
22723
+ if (index !== -1) {
22724
+ const packageDir = file.slice(0, index + marker2.length - 1);
22725
+ const tree = packageDir.slice(0, packageDir.indexOf(`${sep5}node_modules${sep5}`));
22726
+ const isProjectRoot = options.isProjectRoot ?? (() => false);
22727
+ return {
22728
+ kind: "npm",
22729
+ path: packageDir,
22730
+ manager: managerOf(packageDir),
22731
+ local: isProjectRoot(tree)
22732
+ };
22733
+ }
22734
+ if (file.includes(`${sep5}packages${sep5}cli${sep5}`) || file.endsWith(".ts")) {
22735
+ return { kind: "source", path: file, reason: "running from a source checkout" };
22736
+ }
22737
+ return { kind: "unknown", path: file, reason: `cannot tell how ${execPath} was installed` };
22738
+ }
22739
+ function compareVersions(a, b) {
22740
+ const split = (value) => {
22741
+ const [core = "", pre] = value.replace(/^v/, "").split("-");
22742
+ const parts = core.split(".").map((n) => Number.parseInt(n, 10) || 0);
22743
+ return { parts, pre };
22744
+ };
22745
+ const left = split(a);
22746
+ const right = split(b);
22747
+ for (let i = 0;i < 3; i++) {
22748
+ const diff2 = (left.parts[i] ?? 0) - (right.parts[i] ?? 0);
22749
+ if (diff2 !== 0)
22750
+ return diff2 < 0 ? -1 : 1;
22751
+ }
22752
+ if (left.pre === right.pre)
22753
+ return 0;
22754
+ if (left.pre === undefined)
22755
+ return 1;
22756
+ if (right.pre === undefined)
22757
+ return -1;
22758
+ return left.pre < right.pre ? -1 : 1;
22759
+ }
22760
+ async function resolveLatest(kind, fetchImpl) {
22761
+ if (kind === "npm") {
22762
+ const response2 = await fetchImpl(REGISTRY);
22763
+ if (!response2.ok)
22764
+ throw new Error(`npm registry returned ${response2.status}`);
22765
+ const body2 = await response2.json();
22766
+ if (!body2.version)
22767
+ throw new Error("npm registry returned no version");
22768
+ return body2.version;
22769
+ }
22770
+ const response = await fetchImpl(RELEASE);
22771
+ if (!response.ok)
22772
+ throw new Error(`GitHub releases returned ${response.status}`);
22773
+ const body = await response.json();
22774
+ if (!body.tag_name)
22775
+ throw new Error("GitHub returned a release with no tag");
22776
+ return body.tag_name.replace(/^v/, "");
22777
+ }
22778
+ function findChecksum(sums, asset) {
22779
+ for (const line of sums.split(`
22780
+ `)) {
22781
+ const [hash, ...rest] = line.trim().split(/\s+/);
22782
+ const name = rest.join(" ").replace(/^\*/, "");
22783
+ if (hash && name && basename2(name) === asset)
22784
+ return hash.toLowerCase();
22785
+ }
22786
+ return;
22787
+ }
22788
+ var installCommand = {
22789
+ npm: `npm install -g ${PACKAGE}@latest`,
22790
+ bun: `bun add -g ${PACKAGE}@latest`,
22791
+ pnpm: `pnpm add -g ${PACKAGE}@latest`,
22792
+ yarn: `yarn global add ${PACKAGE}@latest`,
22793
+ volta: `volta install ${PACKAGE}@latest`
22794
+ };
22795
+ async function runUpdate(options = {}) {
22796
+ const out = options.out ?? ((text2) => process.stdout.write(text2));
22797
+ const err = options.err ?? ((text2) => process.stderr.write(text2));
22798
+ const current = options.current ?? VERSION;
22799
+ const install = options.install ?? detectInstall({
22800
+ execPath: process.execPath,
22801
+ moduleUrl: import.meta.url,
22802
+ bunVersion: process.versions.bun,
22803
+ platform: process.platform,
22804
+ arch: process.arch,
22805
+ realpath: (path) => {
22806
+ try {
22807
+ return realpathSync(path);
22808
+ } catch {
22809
+ return path;
22810
+ }
22811
+ },
22812
+ isProjectRoot: (dir) => existsSync2(join19(dir, "package.json"))
22813
+ });
22814
+ const fetchImpl = options.fetch ?? ((url) => fetch(url));
22815
+ if (install.kind === "source" || install.kind === "unknown") {
22816
+ err(`earshot update: ${install.reason}
22817
+ `);
22818
+ err(install.kind === "source" ? `update the checkout with git instead
22819
+ ` : `reinstall from ${`https://github.com/${REPO}/releases`}
22820
+ `);
22821
+ return 2;
22822
+ }
22823
+ if (install.kind === "binary" && install.asset === undefined) {
22824
+ err(`earshot update: ${install.reason}
22825
+ `);
22826
+ return 2;
22827
+ }
22828
+ let latest;
22829
+ try {
22830
+ latest = await resolveLatest(install.kind, fetchImpl);
22831
+ } catch (error) {
22832
+ err(`earshot update: ${error.message}
22833
+ `);
22834
+ return 1;
22835
+ }
22836
+ if (compareVersions(current, latest) >= 0) {
22837
+ out(`earshot ${current} is the latest version
22838
+ `);
22839
+ return 0;
22840
+ }
22841
+ const where = install.kind === "npm" ? `installed with ${install.manager}, ${install.local ? "in this project" : "globally"}` : "standalone binary";
22842
+ out(`earshot ${current} -> ${latest} (${where})
22843
+ `);
22844
+ if (install.kind === "npm") {
22845
+ const manager2 = install.manager ?? "npm";
22846
+ const command = install.local ? `${manager2 === "npm" ? "npm install" : `${manager2} add`} ${PACKAGE}@latest` : installCommand[manager2];
22847
+ out(`
22848
+ ${command}
22849
+
22850
+ `);
22851
+ if (install.local || manager2 !== "npm") {
22852
+ out(`run that to update
22853
+ `);
22854
+ return options.check ? 4 : 0;
22855
+ }
22856
+ if (options.check)
22857
+ return 4;
22858
+ if (!await confirmed(options, "run it now?")) {
22859
+ out(`run that to update
22860
+ `);
22861
+ return 0;
22862
+ }
22863
+ const run4 = options.run ?? runCommand2;
22864
+ const result = run4("npm", ["install", "-g", `${PACKAGE}@latest`]);
22865
+ if (result.status !== 0) {
22866
+ err(`earshot update: npm exited ${result.status}
22867
+ `);
22868
+ return 1;
22869
+ }
22870
+ out(`updated to ${latest}
22871
+ `);
22872
+ return 0;
22873
+ }
22874
+ return await updateBinary(install, latest, options, out, err);
22875
+ }
22876
+ function runCommand2(command, args) {
22877
+ const result = spawnSync2(command, args, { stdio: "inherit", windowsHide: true, shell: false });
22878
+ return { status: result.status };
22879
+ }
22880
+ async function confirmed(options, question) {
22881
+ if (options.yes)
22882
+ return true;
22883
+ const confirm = options.confirm ?? defaultConfirm;
22884
+ return await confirm(question);
22885
+ }
22886
+ async function defaultConfirm(question) {
22887
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
22888
+ return false;
22889
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
22890
+ try {
22891
+ const answer = await rl.question(`${question} [y/N] `);
22892
+ return /^y(es)?$/i.test(answer.trim());
22893
+ } finally {
22894
+ rl.close();
22895
+ }
22896
+ }
22897
+ async function updateBinary(install, latest, options, out, err) {
22898
+ const target = install.path;
22899
+ const asset = install.asset;
22900
+ const dir = dirname9(target);
22901
+ const fetchImpl = options.fetch ?? ((url) => fetch(url));
22902
+ await sweepStale(dir, target);
22903
+ out(` ${target} ${asset}
22904
+ `);
22905
+ if (options.check)
22906
+ return 4;
22907
+ try {
22908
+ await writeFile10(join19(dir, `.earshot-update-probe-${process.pid}`), "");
22909
+ await rm2(join19(dir, `.earshot-update-probe-${process.pid}`), { force: true });
22910
+ } catch {
22911
+ err(`earshot update: cannot write to ${dir}
22912
+ `);
22913
+ err(`re-run with the permissions that own that directory
22914
+ `);
22915
+ return 1;
22916
+ }
22917
+ if (!await confirmed(options, `download ${asset} and replace it?`)) {
22918
+ out(`nothing was changed
22919
+ `);
22920
+ return 0;
22921
+ }
22922
+ const base = `https://github.com/${REPO}/releases/download/v${latest}`;
22923
+ const temp = join19(dir, `.earshot-update-${process.pid}.tmp`);
22924
+ try {
22925
+ out(" downloading… ");
22926
+ const download = await fetchImpl(`${base}/${asset}`);
22927
+ if (!download.ok)
22928
+ throw new Error(`downloading ${asset} returned ${download.status}`);
22929
+ const bytes = new Uint8Array(await download.arrayBuffer());
22930
+ out("verifying SHA256… ");
22931
+ const sumsResponse = await fetchImpl(`${base}/SHA256SUMS`);
22932
+ if (!sumsResponse.ok)
22933
+ throw new Error(`SHA256SUMS returned ${sumsResponse.status}`);
22934
+ const expected = findChecksum(await sumsResponse.text(), asset);
22935
+ if (expected === undefined)
22936
+ throw new Error(`SHA256SUMS does not list ${asset}`);
22937
+ const actual = createHash4("sha256").update(bytes).digest("hex");
22938
+ if (actual !== expected) {
22939
+ throw new Error(`checksum mismatch for ${asset}: expected ${expected}, got ${actual}`);
22940
+ }
22941
+ out("replacing… ");
22942
+ await writeFile10(temp, bytes);
22943
+ const platform7 = options.platform ?? process.platform;
22944
+ if (platform7 !== "win32")
22945
+ await chmod(temp, 493);
22946
+ await replace(temp, target, platform7);
22947
+ out(`
22948
+ updated to ${latest}
22949
+ `);
22950
+ return 0;
22951
+ } catch (error) {
22952
+ await rm2(temp, { force: true });
22953
+ out(`
22954
+ `);
22955
+ err(`earshot update: ${error.message}
22956
+ `);
22957
+ err(`nothing was replaced
22958
+ `);
22959
+ return 1;
22960
+ }
22961
+ }
22962
+ var realIo = {
22963
+ rename: (from, to) => rename2(from, to),
22964
+ remove: (path) => rm2(path, { force: true })
22965
+ };
22966
+ async function replace(temp, target, platform7, io = realIo) {
22967
+ if (platform7 !== "win32") {
22968
+ await io.rename(temp, target);
22969
+ return;
22970
+ }
22971
+ const aside = `${target}.old-${process.pid}`;
22972
+ await io.rename(target, aside);
22973
+ try {
22974
+ await io.rename(temp, target);
22975
+ } catch (error) {
22976
+ await io.rename(aside, target).catch(() => {});
22977
+ throw error;
22978
+ }
22979
+ await io.remove(aside).catch(() => {});
22980
+ }
22981
+ async function sweepStale(dir, target) {
22982
+ const prefix = `${basename2(target)}.old-`;
22983
+ const entries = await readdir8(dir).catch(() => []);
22984
+ for (const entry of entries) {
22985
+ if (entry.startsWith(prefix))
22986
+ await rm2(join19(dir, entry), { force: true }).catch(() => {});
22987
+ }
22988
+ }
22989
+ async function updateCommand(args) {
22990
+ if (args.positionals.length > 0) {
22991
+ process.stderr.write(`earshot update: unexpected argument "${args.positionals[0]}"
22992
+ `);
22993
+ process.stderr.write(`usage: earshot update [--check] [--yes]
22994
+ `);
22995
+ return 2;
22996
+ }
22997
+ return runUpdate({
22998
+ check: args.flags.check === true,
22999
+ yes: args.flags.yes === true || args.flags.y === true
23000
+ });
23001
+ }
23002
+
22657
23003
  // packages/cli/src/index.ts
22658
23004
  var HELP = `earshot ${VERSION} - a terminal coding agent that actually listens
22659
23005
 
@@ -22666,6 +23012,7 @@ Usage
22666
23012
  earshot extensions <cmd> list, trust or untrust in-process extensions
22667
23013
  earshot acp serve editor clients over ACP v1 on stdio
22668
23014
  earshot doctor diagnose the local setup
23015
+ earshot update [--check] update earshot to the latest release
22669
23016
 
22670
23017
  Flags
22671
23018
  --model <provider/model> model for this session
@@ -22705,6 +23052,8 @@ async function main(argv = process.argv.slice(2)) {
22705
23052
  return authCommand(args);
22706
23053
  if (command === "doctor")
22707
23054
  return doctorCommand(args);
23055
+ if (command === "update")
23056
+ return updateCommand(args);
22708
23057
  if (command === "acp")
22709
23058
  return acpCommand(args);
22710
23059
  if (command) {
@@ -22719,5 +23068,5 @@ async function main(argv = process.argv.slice(2)) {
22719
23068
  var code = await main();
22720
23069
  process.exitCode = code;
22721
23070
 
22722
- //# debugId=B062C0361E20E53764756E2164756E21
23071
+ //# debugId=8F9F015F9221906F64756E2164756E21
22723
23072
  //# sourceMappingURL=main.js.map