@raegent/earshot 0.2.0 → 0.3.1

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.1";
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,378 @@ ${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
+ function releaseAuth(env) {
22761
+ const token = env.EARSHOT_GITHUB_TOKEN ?? env.GITHUB_TOKEN ?? env.GH_TOKEN;
22762
+ return token ? { headers: { authorization: `Bearer ${token}` } } : {};
22763
+ }
22764
+ function releaseNotFound(env) {
22765
+ return releaseAuth(env).headers === undefined ? "GitHub returned 404. The releases are not public, so this needs a token: " + "set GITHUB_TOKEN (or GH_TOKEN) to one that can read the repository." : "GitHub returned 404 for the token in GITHUB_TOKEN; it may not have access " + "to this repository.";
22766
+ }
22767
+ async function fetchRelease(fetchImpl, env) {
22768
+ const response = await fetchImpl(RELEASE, releaseAuth(env));
22769
+ if (response.status === 404)
22770
+ throw new Error(releaseNotFound(env));
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
+ const assets = {};
22777
+ for (const asset of body.assets ?? [])
22778
+ assets[asset.name] = asset.url;
22779
+ return { version: body.tag_name.replace(/^v/, ""), assets };
22780
+ }
22781
+ async function resolveLatest(kind, fetchImpl, env = process.env) {
22782
+ if (kind === "npm") {
22783
+ const response = await fetchImpl(REGISTRY);
22784
+ if (!response.ok)
22785
+ throw new Error(`npm registry returned ${response.status}`);
22786
+ const body = await response.json();
22787
+ if (!body.version)
22788
+ throw new Error("npm registry returned no version");
22789
+ return body.version;
22790
+ }
22791
+ return (await fetchRelease(fetchImpl, env)).version;
22792
+ }
22793
+ function findChecksum(sums, asset) {
22794
+ for (const line of sums.split(`
22795
+ `)) {
22796
+ const [hash, ...rest] = line.trim().split(/\s+/);
22797
+ const name = rest.join(" ").replace(/^\*/, "");
22798
+ if (hash && name && basename2(name) === asset)
22799
+ return hash.toLowerCase();
22800
+ }
22801
+ return;
22802
+ }
22803
+ var installCommand = {
22804
+ npm: `npm install -g ${PACKAGE}@latest`,
22805
+ bun: `bun add -g ${PACKAGE}@latest`,
22806
+ pnpm: `pnpm add -g ${PACKAGE}@latest`,
22807
+ yarn: `yarn global add ${PACKAGE}@latest`,
22808
+ volta: `volta install ${PACKAGE}@latest`
22809
+ };
22810
+ async function runUpdate(options = {}) {
22811
+ const out = options.out ?? ((text2) => process.stdout.write(text2));
22812
+ const err = options.err ?? ((text2) => process.stderr.write(text2));
22813
+ const current = options.current ?? VERSION;
22814
+ const install = options.install ?? detectInstall({
22815
+ execPath: process.execPath,
22816
+ moduleUrl: import.meta.url,
22817
+ bunVersion: process.versions.bun,
22818
+ platform: process.platform,
22819
+ arch: process.arch,
22820
+ realpath: (path) => {
22821
+ try {
22822
+ return realpathSync(path);
22823
+ } catch {
22824
+ return path;
22825
+ }
22826
+ },
22827
+ isProjectRoot: (dir) => existsSync2(join19(dir, "package.json"))
22828
+ });
22829
+ const fetchImpl = options.fetch ?? ((url, init) => fetch(url, init));
22830
+ if (install.kind === "source" || install.kind === "unknown") {
22831
+ err(`earshot update: ${install.reason}
22832
+ `);
22833
+ err(install.kind === "source" ? `update the checkout with git instead
22834
+ ` : `reinstall from ${`https://github.com/${REPO}/releases`}
22835
+ `);
22836
+ return 2;
22837
+ }
22838
+ if (install.kind === "binary" && install.asset === undefined) {
22839
+ err(`earshot update: ${install.reason}
22840
+ `);
22841
+ return 2;
22842
+ }
22843
+ let latest;
22844
+ let release2;
22845
+ try {
22846
+ if (install.kind === "binary") {
22847
+ release2 = await fetchRelease(fetchImpl, options.env ?? process.env);
22848
+ latest = release2.version;
22849
+ } else {
22850
+ latest = await resolveLatest(install.kind, fetchImpl, options.env ?? process.env);
22851
+ }
22852
+ } catch (error) {
22853
+ err(`earshot update: ${error.message}
22854
+ `);
22855
+ return 1;
22856
+ }
22857
+ if (compareVersions(current, latest) >= 0) {
22858
+ out(`earshot ${current} is the latest version
22859
+ `);
22860
+ return 0;
22861
+ }
22862
+ const where = install.kind === "npm" ? `installed with ${install.manager}, ${install.local ? "in this project" : "globally"}` : "standalone binary";
22863
+ out(`earshot ${current} -> ${latest} (${where})
22864
+ `);
22865
+ if (install.kind === "npm") {
22866
+ const manager2 = install.manager ?? "npm";
22867
+ const command = install.local ? `${manager2 === "npm" ? "npm install" : `${manager2} add`} ${PACKAGE}@latest` : installCommand[manager2];
22868
+ out(`
22869
+ ${command}
22870
+
22871
+ `);
22872
+ if (install.local || manager2 !== "npm") {
22873
+ out(`run that to update
22874
+ `);
22875
+ return options.check ? 4 : 0;
22876
+ }
22877
+ if (options.check)
22878
+ return 4;
22879
+ if (!await confirmed(options, "run it now?")) {
22880
+ out(`run that to update
22881
+ `);
22882
+ return 0;
22883
+ }
22884
+ const run4 = options.run ?? runCommand2;
22885
+ const result = run4("npm", ["install", "-g", `${PACKAGE}@latest`]);
22886
+ if (result.status !== 0) {
22887
+ err(`earshot update: npm exited ${result.status}
22888
+ `);
22889
+ return 1;
22890
+ }
22891
+ out(`updated to ${latest}
22892
+ `);
22893
+ return 0;
22894
+ }
22895
+ return await updateBinary(install, release2, options, out, err);
22896
+ }
22897
+ function runCommand2(command, args) {
22898
+ const result = spawnSync2(command, args, { stdio: "inherit", windowsHide: true, shell: false });
22899
+ return { status: result.status };
22900
+ }
22901
+ async function confirmed(options, question) {
22902
+ if (options.yes)
22903
+ return true;
22904
+ const confirm = options.confirm ?? defaultConfirm;
22905
+ return await confirm(question);
22906
+ }
22907
+ async function defaultConfirm(question) {
22908
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
22909
+ return false;
22910
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
22911
+ try {
22912
+ const answer = await rl.question(`${question} [y/N] `);
22913
+ return /^y(es)?$/i.test(answer.trim());
22914
+ } finally {
22915
+ rl.close();
22916
+ }
22917
+ }
22918
+ async function updateBinary(install, release2, options, out, err) {
22919
+ const target = install.path;
22920
+ const asset = install.asset;
22921
+ const dir = dirname9(target);
22922
+ const latest = release2.version;
22923
+ const fetchImpl = options.fetch ?? ((url, init) => fetch(url, init));
22924
+ await sweepStale(dir, target);
22925
+ out(` ${target} ${asset}
22926
+ `);
22927
+ if (options.check)
22928
+ return 4;
22929
+ try {
22930
+ await writeFile10(join19(dir, `.earshot-update-probe-${process.pid}`), "");
22931
+ await rm2(join19(dir, `.earshot-update-probe-${process.pid}`), { force: true });
22932
+ } catch {
22933
+ err(`earshot update: cannot write to ${dir}
22934
+ `);
22935
+ err(`re-run with the permissions that own that directory
22936
+ `);
22937
+ return 1;
22938
+ }
22939
+ if (!await confirmed(options, `download ${asset} and replace it?`)) {
22940
+ out(`nothing was changed
22941
+ `);
22942
+ return 0;
22943
+ }
22944
+ const assetUrl = release2.assets[asset];
22945
+ const sumsUrl = release2.assets.SHA256SUMS;
22946
+ if (assetUrl === undefined || sumsUrl === undefined) {
22947
+ err(`earshot update: release v${latest} does not publish ${assetUrl ? "SHA256SUMS" : asset}
22948
+ `);
22949
+ return 1;
22950
+ }
22951
+ const temp = join19(dir, `.earshot-update-${process.pid}.tmp`);
22952
+ try {
22953
+ const auth2 = releaseAuth(options.env ?? process.env);
22954
+ const octet = {
22955
+ ...auth2,
22956
+ headers: { ...auth2.headers, accept: "application/octet-stream" }
22957
+ };
22958
+ out(" downloading… ");
22959
+ const download = await fetchImpl(assetUrl, octet);
22960
+ if (download.status === 404)
22961
+ throw new Error(releaseNotFound(options.env ?? process.env));
22962
+ if (!download.ok)
22963
+ throw new Error(`downloading ${asset} returned ${download.status}`);
22964
+ const bytes = new Uint8Array(await download.arrayBuffer());
22965
+ out("verifying SHA256… ");
22966
+ const sumsResponse = await fetchImpl(sumsUrl, octet);
22967
+ if (!sumsResponse.ok)
22968
+ throw new Error(`SHA256SUMS returned ${sumsResponse.status}`);
22969
+ const expected = findChecksum(await sumsResponse.text(), asset);
22970
+ if (expected === undefined)
22971
+ throw new Error(`SHA256SUMS does not list ${asset}`);
22972
+ const actual = createHash4("sha256").update(bytes).digest("hex");
22973
+ if (actual !== expected) {
22974
+ throw new Error(`checksum mismatch for ${asset}: expected ${expected}, got ${actual}`);
22975
+ }
22976
+ out("replacing… ");
22977
+ await writeFile10(temp, bytes);
22978
+ const platform7 = options.platform ?? process.platform;
22979
+ if (platform7 !== "win32")
22980
+ await chmod(temp, 493);
22981
+ await replace(temp, target, platform7);
22982
+ out(`
22983
+ updated to ${latest}
22984
+ `);
22985
+ return 0;
22986
+ } catch (error) {
22987
+ await rm2(temp, { force: true });
22988
+ out(`
22989
+ `);
22990
+ err(`earshot update: ${error.message}
22991
+ `);
22992
+ err(`nothing was replaced
22993
+ `);
22994
+ return 1;
22995
+ }
22996
+ }
22997
+ var realIo = {
22998
+ rename: (from, to) => rename2(from, to),
22999
+ remove: (path) => rm2(path, { force: true })
23000
+ };
23001
+ async function replace(temp, target, platform7, io = realIo) {
23002
+ if (platform7 !== "win32") {
23003
+ await io.rename(temp, target);
23004
+ return;
23005
+ }
23006
+ const aside = `${target}.old-${process.pid}`;
23007
+ await io.rename(target, aside);
23008
+ try {
23009
+ await io.rename(temp, target);
23010
+ } catch (error) {
23011
+ await io.rename(aside, target).catch(() => {});
23012
+ throw error;
23013
+ }
23014
+ await io.remove(aside).catch(() => {});
23015
+ }
23016
+ async function sweepStale(dir, target) {
23017
+ const prefix = `${basename2(target)}.old-`;
23018
+ const entries = await readdir8(dir).catch(() => []);
23019
+ for (const entry of entries) {
23020
+ if (entry.startsWith(prefix))
23021
+ await rm2(join19(dir, entry), { force: true }).catch(() => {});
23022
+ }
23023
+ }
23024
+ async function updateCommand(args) {
23025
+ if (args.positionals.length > 0) {
23026
+ process.stderr.write(`earshot update: unexpected argument "${args.positionals[0]}"
23027
+ `);
23028
+ process.stderr.write(`usage: earshot update [--check] [--yes]
23029
+ `);
23030
+ return 2;
23031
+ }
23032
+ return runUpdate({
23033
+ check: args.flags.check === true,
23034
+ yes: args.flags.yes === true || args.flags.y === true
23035
+ });
23036
+ }
23037
+
22657
23038
  // packages/cli/src/index.ts
22658
23039
  var HELP = `earshot ${VERSION} - a terminal coding agent that actually listens
22659
23040
 
@@ -22666,6 +23047,7 @@ Usage
22666
23047
  earshot extensions <cmd> list, trust or untrust in-process extensions
22667
23048
  earshot acp serve editor clients over ACP v1 on stdio
22668
23049
  earshot doctor diagnose the local setup
23050
+ earshot update [--check] update earshot to the latest release
22669
23051
 
22670
23052
  Flags
22671
23053
  --model <provider/model> model for this session
@@ -22705,6 +23087,8 @@ async function main(argv = process.argv.slice(2)) {
22705
23087
  return authCommand(args);
22706
23088
  if (command === "doctor")
22707
23089
  return doctorCommand(args);
23090
+ if (command === "update")
23091
+ return updateCommand(args);
22708
23092
  if (command === "acp")
22709
23093
  return acpCommand(args);
22710
23094
  if (command) {
@@ -22719,5 +23103,5 @@ async function main(argv = process.argv.slice(2)) {
22719
23103
  var code = await main();
22720
23104
  process.exitCode = code;
22721
23105
 
22722
- //# debugId=B062C0361E20E53764756E2164756E21
23106
+ //# debugId=6AB54613CF301A9064756E2164756E21
22723
23107
  //# sourceMappingURL=main.js.map