@sunilaleti/hx 1.0.0 → 1.1.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/CHANGELOG.md +37 -1
- package/README.md +12 -5
- package/dist/hx.js +195 -20
- package/dist/hx.js.map +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,40 @@ the project follows [semantic versioning](https://semver.org/).
|
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
+
## [1.1.1] - 2026-10-31
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- HTTPS failed on macOS for every host - OpenRouter, ChatGPT, Tavily, npm - with "unable to get
|
|
36
|
+
local issuer certificate". HX verified against the OS trust store alone, and on macOS Node reads
|
|
37
|
+
only the certificates trusted by hand there, not the system roots. HX now trusts Node's own
|
|
38
|
+
roots and the OS store together, so a corporate CA in the keychain still works.
|
|
39
|
+
- A certificate HX will not accept is reported with what to do about it - add the CA to the
|
|
40
|
+
system trust store or set `HX_CA_BUNDLE` - instead of Node's advice to restart it with
|
|
41
|
+
`--use-system-ca`, and is not retried: another attempt meets the same certificate.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## [1.1.0] - 2026-10-31
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- HX updates itself, as Claude Code does. About once a day a session checks npm for a newer
|
|
50
|
+
release, installs it in the background with `npm install --global`, and says to restart.
|
|
51
|
+
`hx update` (or `hx upgrade`) updates now. Turn the background update off with
|
|
52
|
+
`"autoUpdates": false` in settings or `HX_DISABLE_AUTOUPDATER=1`. Only an npm install updates
|
|
53
|
+
itself; `hx -p` never does.
|
|
54
|
+
1.0.0 has no updater: install 1.1.0 once with `npm install --global @sunilaleti/hx@latest`, and
|
|
55
|
+
it keeps itself current from there.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- CI runs the suite twice instead of four times - Linux and macOS, both on Node 24 - and
|
|
60
|
+
publishes with the npm that ships with Node 24. Node 22, the oldest HX supports, is checked
|
|
61
|
+
by installing the package on it and running `hx --version`.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
31
65
|
## [1.0.0] - 2026-10-31
|
|
32
66
|
|
|
33
67
|
### Changed
|
|
@@ -1103,7 +1137,9 @@ First release, published to PyPI as [`hx-cli`](https://pypi.org/project/hx-cli/)
|
|
|
1103
1137
|
- `/configure` and `hx auth` for the OpenRouter key, `hx upgrade` for
|
|
1104
1138
|
self-update, and `install.sh` bootstrapping uv with a pinned Python.
|
|
1105
1139
|
|
|
1106
|
-
[Unreleased]: https://github.com/aletisunil/hx/compare/v1.
|
|
1140
|
+
[Unreleased]: https://github.com/aletisunil/hx/compare/v1.1.1...HEAD
|
|
1141
|
+
[1.1.1]: https://github.com/aletisunil/hx/compare/v1.1.0...v1.1.1
|
|
1142
|
+
[1.1.0]: https://github.com/aletisunil/hx/compare/v1.0.0...v1.1.0
|
|
1107
1143
|
[1.0.0]: https://github.com/aletisunil/hx/compare/v0.3.0-beta.1...v1.0.0
|
|
1108
1144
|
[0.3.0-beta.1]: https://github.com/aletisunil/hx/compare/v0.2.20...v0.3.0-beta.1
|
|
1109
1145
|
[0.2.20]: https://github.com/aletisunil/hx/compare/v0.2.19...v0.2.20
|
package/README.md
CHANGED
|
@@ -16,8 +16,13 @@ npm install --global @sunilaleti/hx
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
HX runs on [Node.js](https://nodejs.org/) 22.15 or newer, on macOS and Linux.
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
Then run `hx`: the first time, it opens with the HX logo and says to type
|
|
20
|
+
`/login`.
|
|
21
|
+
|
|
22
|
+
HX keeps itself up to date, as Claude Code does. About once a day, a session
|
|
23
|
+
checks npm for a newer release, installs it in the background, and says to
|
|
24
|
+
restart. Run `hx update` to update now. To turn the background update off, set
|
|
25
|
+
`"autoUpdates": false` in `~/.hx/settings.json`, or `HX_DISABLE_AUTOUPDATER=1`.
|
|
21
26
|
|
|
22
27
|
Or let the install script check your Node.js version and run that command:
|
|
23
28
|
|
|
@@ -288,9 +293,9 @@ number.
|
|
|
288
293
|
Credits are *not* in the status bar's cost, which prices tokens: `/cost`
|
|
289
294
|
under-reports a session that searched.
|
|
290
295
|
|
|
291
|
-
**Behind a TLS-inspecting proxy.** HX verifies certificates against
|
|
292
|
-
operating system's trust store, so a corporate CA the
|
|
293
|
-
needs no setup. When the CA lives only in a file, name it:
|
|
296
|
+
**Behind a TLS-inspecting proxy.** HX verifies certificates against Node's own
|
|
297
|
+
roots and the operating system's trust store together, so a corporate CA the
|
|
298
|
+
machine already trusts needs no setup. When the CA lives only in a file, name it:
|
|
294
299
|
|
|
295
300
|
```sh
|
|
296
301
|
export HX_CA_BUNDLE=/path/to/corp-ca.pem # SSL_CERT_FILE and REQUESTS_CA_BUNDLE also work
|
|
@@ -349,6 +354,7 @@ hx auth login [provider] # sign in (openrouter, openai-codex, devin, claude-c
|
|
|
349
354
|
hx auth logout <provider> # forget a stored credential
|
|
350
355
|
hx docs [section] # the manual; no argument lists its sections
|
|
351
356
|
hx changelog [version] # what shipped in each version
|
|
357
|
+
hx update # install the newest release now (also: hx upgrade)
|
|
352
358
|
```
|
|
353
359
|
|
|
354
360
|
Print mode is the scriptable one: stdout carries only the assistant's text, so
|
|
@@ -663,6 +669,7 @@ along with the `.hx/.gitignore` it added to hide it.
|
|
|
663
669
|
{
|
|
664
670
|
"theme": "dark", // dark | light | ansi, or a file in ~/.hx/themes
|
|
665
671
|
"quiet_startup": false, // skip the startup header
|
|
672
|
+
"autoUpdates": true, // install new releases in the background, once a day
|
|
666
673
|
|
|
667
674
|
"models": {
|
|
668
675
|
"model": "anthropic/claude-sonnet-4.5",
|
package/dist/hx.js
CHANGED
|
@@ -534,10 +534,8 @@ function tlsAgentOptions() {
|
|
|
534
534
|
const bundle = caBundle();
|
|
535
535
|
let ca;
|
|
536
536
|
if (bundle !== null) ca = certificates(readText(bundle));
|
|
537
|
-
else
|
|
538
|
-
ca = tls.getCACertificates("system");
|
|
539
|
-
if (ca.length === 0) ca = tls.getCACertificates("default");
|
|
540
|
-
}
|
|
537
|
+
else
|
|
538
|
+
ca = [.../* @__PURE__ */ new Set([...tls.getCACertificates("default"), ...tls.getCACertificates("system")])];
|
|
541
539
|
tlsOptions = { ca, ALPNProtocols: ["http/1.1"] };
|
|
542
540
|
}
|
|
543
541
|
return tlsOptions;
|
|
@@ -582,12 +580,10 @@ function withTlsHint(message2) {
|
|
|
582
580
|
return `${rstrip(rstrip(message2), ".")}. ${hint2}`;
|
|
583
581
|
}
|
|
584
582
|
function describe(error) {
|
|
583
|
+
const certificate = certificateFailure(error);
|
|
584
|
+
if (certificate !== null) return certificate;
|
|
585
585
|
for (const err of chain(error)) {
|
|
586
586
|
const code2 = err.code ?? "";
|
|
587
|
-
if (VERIFY_CODES.has(code2)) {
|
|
588
|
-
const reason = flatten(err.message || "certificate verify failed");
|
|
589
|
-
return `TLS certificate verification failed (${reason}). ${tlsHint(reason)}`;
|
|
590
|
-
}
|
|
591
587
|
if (code2.startsWith("ERR_SSL") || code2.startsWith("ERR_TLS")) {
|
|
592
588
|
return `TLS handshake failed: ${flatten(err.message)}.`;
|
|
593
589
|
}
|
|
@@ -599,6 +595,19 @@ function describe(error) {
|
|
|
599
595
|
const failure2 = error;
|
|
600
596
|
return withTlsHint(`${failure2?.name ?? "Error"}: ${flatten(String(failure2?.message ?? error))}`);
|
|
601
597
|
}
|
|
598
|
+
function certificateFailure(error) {
|
|
599
|
+
for (const err of chain(error)) {
|
|
600
|
+
if (!VERIFY_CODES.has(err.code ?? "")) continue;
|
|
601
|
+
const reason = flatten(
|
|
602
|
+
(err.message || "certificate verify failed").replace(
|
|
603
|
+
/;\s*if the root CA[\s\S]*$/,
|
|
604
|
+
""
|
|
605
|
+
)
|
|
606
|
+
);
|
|
607
|
+
return `TLS certificate verification failed (${reason}). ${tlsHint(reason)}`;
|
|
608
|
+
}
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
602
611
|
function chain(error) {
|
|
603
612
|
const seen = [];
|
|
604
613
|
let current = error;
|
|
@@ -1725,11 +1734,17 @@ function streamEnd(stopReason, fields = {}) {
|
|
|
1725
1734
|
planWindows: fields.planWindows ?? []
|
|
1726
1735
|
};
|
|
1727
1736
|
}
|
|
1737
|
+
function unreached(error) {
|
|
1738
|
+
const certificate = certificateFailure(error);
|
|
1739
|
+
if (certificate !== null) return new ProviderError(certificate);
|
|
1740
|
+
return new ProviderError(error.message, { retryable: true });
|
|
1741
|
+
}
|
|
1728
1742
|
var delta, ProviderError;
|
|
1729
1743
|
var init_base = __esm({
|
|
1730
1744
|
"src/providers/base.ts"() {
|
|
1731
1745
|
"use strict";
|
|
1732
1746
|
init_usage();
|
|
1747
|
+
init_net();
|
|
1733
1748
|
delta = (fields) => ({
|
|
1734
1749
|
kind: "delta",
|
|
1735
1750
|
...fields
|
|
@@ -3288,7 +3303,7 @@ var init_openrouter = __esm({
|
|
|
3288
3303
|
});
|
|
3289
3304
|
} catch (error) {
|
|
3290
3305
|
if (signal.aborted) throw error;
|
|
3291
|
-
throw
|
|
3306
|
+
throw unreached(error);
|
|
3292
3307
|
}
|
|
3293
3308
|
try {
|
|
3294
3309
|
if (response.status >= 400) {
|
|
@@ -6351,7 +6366,8 @@ function buildSettings(data, cwd) {
|
|
|
6351
6366
|
tui: { enterWhileBusy: busyRaw, fullscreen: truthy(tui.fullscreen ?? false) },
|
|
6352
6367
|
tools: { hashline: truthy(pick(tools, "hashline", true)) },
|
|
6353
6368
|
theme: pick(data, "theme", "dark"),
|
|
6354
|
-
quietStartup: truthy(pick(data, "quietStartup", pick(data, "quiet_startup", false)))
|
|
6369
|
+
quietStartup: truthy(pick(data, "quietStartup", pick(data, "quiet_startup", false))),
|
|
6370
|
+
autoUpdates: truthy(pick(data, "autoUpdates", pick(data, "auto_updates", true)))
|
|
6355
6371
|
};
|
|
6356
6372
|
}
|
|
6357
6373
|
function iterate(value) {
|
|
@@ -7189,7 +7205,7 @@ async function unaryCall(baseUrl, path, body, operation, timeout = 600, signal)
|
|
|
7189
7205
|
[status3, payload] = await post(`${baseUrl}${path}`, body, UNARY_HEADERS, timeout, signal);
|
|
7190
7206
|
} catch (error) {
|
|
7191
7207
|
if (signal?.aborted) throw error;
|
|
7192
|
-
throw
|
|
7208
|
+
throw unreached(error);
|
|
7193
7209
|
}
|
|
7194
7210
|
if (status3 >= 400) throw httpError(operation, status3, payload);
|
|
7195
7211
|
return payload;
|
|
@@ -7485,7 +7501,7 @@ var init_devin2 = __esm({
|
|
|
7485
7501
|
});
|
|
7486
7502
|
} catch (error) {
|
|
7487
7503
|
if (signal.aborted) throw error;
|
|
7488
|
-
throw
|
|
7504
|
+
throw unreached(error);
|
|
7489
7505
|
}
|
|
7490
7506
|
try {
|
|
7491
7507
|
if (response.status >= 400) {
|
|
@@ -67732,7 +67748,8 @@ init_net();
|
|
|
67732
67748
|
init_py();
|
|
67733
67749
|
|
|
67734
67750
|
// src/version.ts
|
|
67735
|
-
var VERSION = "1.
|
|
67751
|
+
var VERSION = "1.1.1";
|
|
67752
|
+
var PACKAGE = "@sunilaleti/hx";
|
|
67736
67753
|
|
|
67737
67754
|
// src/providers/codex.ts
|
|
67738
67755
|
init_base();
|
|
@@ -67803,7 +67820,7 @@ var CodexProvider = class {
|
|
|
67803
67820
|
});
|
|
67804
67821
|
} catch (error) {
|
|
67805
67822
|
if (signal.aborted) throw error;
|
|
67806
|
-
throw
|
|
67823
|
+
throw unreached(error);
|
|
67807
67824
|
}
|
|
67808
67825
|
const windows = planWindows(response.headers);
|
|
67809
67826
|
try {
|
|
@@ -75807,11 +75824,11 @@ var LANGUAGES = {
|
|
|
75807
75824
|
".go": GO,
|
|
75808
75825
|
".rs": RUST
|
|
75809
75826
|
};
|
|
75810
|
-
var
|
|
75827
|
+
var PACKAGE2 = "@vscode/tree-sitter-wasm";
|
|
75811
75828
|
var load = createRequire(import.meta.url);
|
|
75812
75829
|
function packageDir() {
|
|
75813
75830
|
try {
|
|
75814
|
-
return dirname(load.resolve(
|
|
75831
|
+
return dirname(load.resolve(PACKAGE2));
|
|
75815
75832
|
} catch {
|
|
75816
75833
|
return null;
|
|
75817
75834
|
}
|
|
@@ -75825,9 +75842,9 @@ function parserFor(grammar) {
|
|
|
75825
75842
|
parser = (async () => {
|
|
75826
75843
|
const directory = packageDir();
|
|
75827
75844
|
if (directory === null)
|
|
75828
|
-
throw new ToolError(`tree-sitter is not installed: npm install -g ${
|
|
75845
|
+
throw new ToolError(`tree-sitter is not installed: npm install -g ${PACKAGE2}`);
|
|
75829
75846
|
runtime ??= (async () => {
|
|
75830
|
-
const module = load(
|
|
75847
|
+
const module = load(PACKAGE2);
|
|
75831
75848
|
await module.Parser.init({ locateFile: (file) => join(directory, file) });
|
|
75832
75849
|
return module;
|
|
75833
75850
|
})();
|
|
@@ -89678,6 +89695,17 @@ var HXSession = class {
|
|
|
89678
89695
|
this.refreshStatus();
|
|
89679
89696
|
if (this.loop.session.activeMessages().length) this.replayTranscript();
|
|
89680
89697
|
if (this.extra.signIn) this.showSignIn(this.extra.signIn);
|
|
89698
|
+
void this.extra.update?.then((outcome2) => {
|
|
89699
|
+
if (outcome2 === null) return;
|
|
89700
|
+
const level = outcome2.ok ? "success" : "warning";
|
|
89701
|
+
if (this.view.transcript.blocks.length) {
|
|
89702
|
+
this.notice(outcome2.message, level);
|
|
89703
|
+
return;
|
|
89704
|
+
}
|
|
89705
|
+
this.view.transcript.add(new Spacer(1));
|
|
89706
|
+
this.view.transcript.add(new Notice(outcome2.message, level));
|
|
89707
|
+
this.runner.requestRender();
|
|
89708
|
+
});
|
|
89681
89709
|
if (this.loop.permissions !== null)
|
|
89682
89710
|
this.loop.permissions.asker = (request2) => this.askPermission(request2);
|
|
89683
89711
|
const events = this.consumeEvents();
|
|
@@ -90680,6 +90708,145 @@ async function runSession(loop, bus, settings, extra = {}) {
|
|
|
90680
90708
|
return session.relaunch;
|
|
90681
90709
|
}
|
|
90682
90710
|
|
|
90711
|
+
// src/update.ts
|
|
90712
|
+
init_json();
|
|
90713
|
+
init_net();
|
|
90714
|
+
init_pathlib();
|
|
90715
|
+
init_paths();
|
|
90716
|
+
init_py();
|
|
90717
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
90718
|
+
import { mkdirSync as mkdirSync15, openSync as openSync10, readFileSync as readFileSync10, realpathSync as realpathSync3, writeFileSync as writeFileSync12 } from "node:fs";
|
|
90719
|
+
init_which();
|
|
90720
|
+
var RELEASES_URL2 = `https://registry.npmjs.org/-/package/${PACKAGE}/dist-tags`;
|
|
90721
|
+
var RELEASES_ENV2 = "HX_RELEASES_URL";
|
|
90722
|
+
var DISABLE_ENV = "HX_DISABLE_AUTOUPDATER";
|
|
90723
|
+
var RELEASES_TIMEOUT2 = 5;
|
|
90724
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
90725
|
+
var stateFile = () => join(userHome(), "update.json");
|
|
90726
|
+
async function latestRelease(signal) {
|
|
90727
|
+
const url = strip(process.env[RELEASES_ENV2] ?? "") || RELEASES_URL2;
|
|
90728
|
+
try {
|
|
90729
|
+
const response = await request(url, {
|
|
90730
|
+
timeout: RELEASES_TIMEOUT2,
|
|
90731
|
+
...signal ? { signal } : {}
|
|
90732
|
+
});
|
|
90733
|
+
try {
|
|
90734
|
+
if (response.status < 200 || response.status >= 300) return null;
|
|
90735
|
+
const tags = await response.json();
|
|
90736
|
+
const latest = typeof tags === "object" && tags !== null ? tags.latest : null;
|
|
90737
|
+
return typeof latest === "string" && RELEASE2.test(latest) ? latest : null;
|
|
90738
|
+
} finally {
|
|
90739
|
+
response.close();
|
|
90740
|
+
}
|
|
90741
|
+
} catch {
|
|
90742
|
+
return null;
|
|
90743
|
+
}
|
|
90744
|
+
}
|
|
90745
|
+
var RELEASE2 = /^\d+\.\d+\.\d+$/;
|
|
90746
|
+
function compareReleases(a, b) {
|
|
90747
|
+
const left = a.split(".").map(Number);
|
|
90748
|
+
const right = b.split(".").map(Number);
|
|
90749
|
+
for (let index = 0; index < 3; index++) {
|
|
90750
|
+
const difference = (left[index] ?? 0) - (right[index] ?? 0);
|
|
90751
|
+
if (difference) return difference;
|
|
90752
|
+
}
|
|
90753
|
+
return 0;
|
|
90754
|
+
}
|
|
90755
|
+
function notAnNpmInstall() {
|
|
90756
|
+
const script = process.argv[1] ?? "";
|
|
90757
|
+
let real2;
|
|
90758
|
+
try {
|
|
90759
|
+
real2 = realpathSync3(script);
|
|
90760
|
+
} catch {
|
|
90761
|
+
return script;
|
|
90762
|
+
}
|
|
90763
|
+
return real2.includes(`/node_modules/${PACKAGE}/`) ? null : parent(parent(real2));
|
|
90764
|
+
}
|
|
90765
|
+
function npm() {
|
|
90766
|
+
return which("npm") ?? join(parent(process.execPath), "npm");
|
|
90767
|
+
}
|
|
90768
|
+
var INSTALL_COMMAND = `npm install --global ${PACKAGE}`;
|
|
90769
|
+
function runNpm(version, background) {
|
|
90770
|
+
const args = ["install", "--global", `${PACKAGE}@${version}`];
|
|
90771
|
+
let stdio = "inherit";
|
|
90772
|
+
if (background) {
|
|
90773
|
+
mkdirSync15(logsDir(), { recursive: true });
|
|
90774
|
+
const log = openSync10(join(logsDir(), "update.log"), "a");
|
|
90775
|
+
stdio = ["ignore", log, log];
|
|
90776
|
+
}
|
|
90777
|
+
return new Promise((resolve2) => {
|
|
90778
|
+
const child = spawn10(npm(), args, { stdio, detached: background });
|
|
90779
|
+
child.once("error", () => resolve2(null));
|
|
90780
|
+
child.once("exit", (code2, signal) => resolve2(code2 ?? (signal ? 1 : 0)));
|
|
90781
|
+
if (background) child.unref();
|
|
90782
|
+
});
|
|
90783
|
+
}
|
|
90784
|
+
async function runUpdate(print2) {
|
|
90785
|
+
const elsewhere2 = notAnNpmInstall();
|
|
90786
|
+
if (elsewhere2 !== null) {
|
|
90787
|
+
print2(
|
|
90788
|
+
`hx is running from ${elsewhere2}, not from an npm install, so it cannot update itself. Update a checkout with git pull and npm run build, or install the release: ${INSTALL_COMMAND}`,
|
|
90789
|
+
process.stderr
|
|
90790
|
+
);
|
|
90791
|
+
return 1;
|
|
90792
|
+
}
|
|
90793
|
+
const latest = await latestRelease();
|
|
90794
|
+
if (latest === null) {
|
|
90795
|
+
print2("Could not reach the npm registry to check for a newer hx.", process.stderr);
|
|
90796
|
+
return 1;
|
|
90797
|
+
}
|
|
90798
|
+
if (compareReleases(latest, VERSION) <= 0) {
|
|
90799
|
+
print2(`hx ${VERSION} is up to date.`);
|
|
90800
|
+
return 0;
|
|
90801
|
+
}
|
|
90802
|
+
print2(`Updating hx ${VERSION} to ${latest}...`);
|
|
90803
|
+
const status3 = await runNpm(latest, false);
|
|
90804
|
+
if (status3 === null) {
|
|
90805
|
+
print2(
|
|
90806
|
+
`npm is not installed, so hx cannot update itself. See https://nodejs.org/`,
|
|
90807
|
+
process.stderr
|
|
90808
|
+
);
|
|
90809
|
+
return 1;
|
|
90810
|
+
}
|
|
90811
|
+
if (status3 !== 0) {
|
|
90812
|
+
print2(`npm could not install ${PACKAGE}@${latest} (exit ${status3}).`, process.stderr);
|
|
90813
|
+
return 1;
|
|
90814
|
+
}
|
|
90815
|
+
print2(`Updated hx to ${latest}.`);
|
|
90816
|
+
return 0;
|
|
90817
|
+
}
|
|
90818
|
+
function lastChecked() {
|
|
90819
|
+
try {
|
|
90820
|
+
const state = JSON.parse(readFileSync10(stateFile(), "utf8"));
|
|
90821
|
+
const at = typeof state === "object" && state !== null ? state.checkedAt : 0;
|
|
90822
|
+
return typeof at === "number" ? at : 0;
|
|
90823
|
+
} catch {
|
|
90824
|
+
return 0;
|
|
90825
|
+
}
|
|
90826
|
+
}
|
|
90827
|
+
async function autoUpdate(enabled, signal) {
|
|
90828
|
+
const disabled = strip(process.env[DISABLE_ENV] ?? "").toLowerCase();
|
|
90829
|
+
if (!enabled || ["1", "true", "yes"].includes(disabled)) return null;
|
|
90830
|
+
if (notAnNpmInstall() !== null) return null;
|
|
90831
|
+
const now3 = Date.now();
|
|
90832
|
+
if (now3 - lastChecked() < CHECK_INTERVAL_MS) return null;
|
|
90833
|
+
try {
|
|
90834
|
+
writeFileSync12(stateFile(), `${dumps({ checkedAt: now3 })}
|
|
90835
|
+
`);
|
|
90836
|
+
} catch {
|
|
90837
|
+
return null;
|
|
90838
|
+
}
|
|
90839
|
+
const latest = await latestRelease(signal);
|
|
90840
|
+
if (signal.aborted || latest === null || compareReleases(latest, VERSION) <= 0) return null;
|
|
90841
|
+
const status3 = await runNpm(latest, true);
|
|
90842
|
+
if (status3 === 0) return { message: `Updated hx to ${latest}. Restart hx to use it.`, ok: true };
|
|
90843
|
+
const why = status3 === null ? "npm could not be started" : `npm exited ${status3}`;
|
|
90844
|
+
return {
|
|
90845
|
+
message: `Could not update hx to ${latest}: ${why}. Run hx update to see why.`,
|
|
90846
|
+
ok: false
|
|
90847
|
+
};
|
|
90848
|
+
}
|
|
90849
|
+
|
|
90683
90850
|
// src/cli.ts
|
|
90684
90851
|
var USAGE = `hx - an agent harness for the terminal
|
|
90685
90852
|
|
|
@@ -90695,6 +90862,7 @@ Usage:
|
|
|
90695
90862
|
hx auth [set|clear] Show or change the OpenRouter API key
|
|
90696
90863
|
hx docs [SECTION|--all] Print the manual, or one section of it
|
|
90697
90864
|
hx changelog [VERSION] Print what shipped in each version
|
|
90865
|
+
hx update Update hx to the newest release
|
|
90698
90866
|
hx --version Show version
|
|
90699
90867
|
hx --help Show this message
|
|
90700
90868
|
|
|
@@ -90758,6 +90926,9 @@ ${USAGE}`, process.stderr);
|
|
|
90758
90926
|
return runMcpCommand(parsed.rest);
|
|
90759
90927
|
case "trace":
|
|
90760
90928
|
return runTraceCommand(parsed);
|
|
90929
|
+
case "update":
|
|
90930
|
+
case "upgrade":
|
|
90931
|
+
return runUpdate(print);
|
|
90761
90932
|
default:
|
|
90762
90933
|
return runTuiCommand(parsed);
|
|
90763
90934
|
}
|
|
@@ -90816,7 +90987,7 @@ function parseArgs(args) {
|
|
|
90816
90987
|
}
|
|
90817
90988
|
if (positional.length) {
|
|
90818
90989
|
const [head4, ...tail3] = positional;
|
|
90819
|
-
if (["resume", "prompt", "trace", "mcp", "auth"].includes(head4)) {
|
|
90990
|
+
if (["resume", "prompt", "trace", "mcp", "auth", "update", "upgrade"].includes(head4)) {
|
|
90820
90991
|
parsed.command = head4;
|
|
90821
90992
|
parsed.rest = tail3;
|
|
90822
90993
|
if (head4 === "resume" && tail3.length) parsed.sessionId = tail3[0];
|
|
@@ -91505,7 +91676,9 @@ async function runTuiCommand(parsed) {
|
|
|
91505
91676
|
if (moved) runtime2.notices.push(moved);
|
|
91506
91677
|
const signIn2 = runtime2.provider instanceof DeferredProvider ? signInHint(runtime2.loop.model, runtime2.auth) : null;
|
|
91507
91678
|
let relaunch = null;
|
|
91679
|
+
const sessionOver = new AbortController();
|
|
91508
91680
|
try {
|
|
91681
|
+
const update = autoUpdate(runtime2.settings.autoUpdates, sessionOver.signal);
|
|
91509
91682
|
await runtime2.connectMcp();
|
|
91510
91683
|
await runtime2.refreshModelsIfStale();
|
|
91511
91684
|
relaunch = await runSession(runtime2.loop, runtime2.bus, runtime2.settings, {
|
|
@@ -91519,9 +91692,11 @@ async function runTuiCommand(parsed) {
|
|
|
91519
91692
|
notices: runtime2.notices,
|
|
91520
91693
|
checkpoints: runtime2.checkpoints,
|
|
91521
91694
|
renameLeft,
|
|
91522
|
-
signIn: signIn2
|
|
91695
|
+
signIn: signIn2,
|
|
91696
|
+
update
|
|
91523
91697
|
});
|
|
91524
91698
|
} finally {
|
|
91699
|
+
sessionOver.abort();
|
|
91525
91700
|
runtime2.bus.close();
|
|
91526
91701
|
await runtime2.aclose();
|
|
91527
91702
|
}
|