aether-code 0.32.2 → 0.33.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/bin/aether-code.js +1 -1
- package/package.json +2 -2
- package/src/agent.js +119 -17
- package/src/box-input.js +209 -132
- package/src/render.js +45 -9
- package/src/repl.js +249 -291
- package/src/update-check.js +117 -60
package/src/update-check.js
CHANGED
|
@@ -1,60 +1,117 @@
|
|
|
1
|
-
// Startup update check
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { readFileSync } from "node:fs";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { dirname, join } from "node:path";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
1
|
+
// Startup update check + auto-update. On every REPL launch, checks the npm
|
|
2
|
+
// registry and silently installs the latest version in the background if one
|
|
3
|
+
// exists. Never throws, bounded by a short timeout so it can't slow startup.
|
|
4
|
+
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
import { c } from "./render.js";
|
|
11
|
+
|
|
12
|
+
export function currentVersion() {
|
|
13
|
+
try {
|
|
14
|
+
const p = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
15
|
+
return JSON.parse(readFileSync(p, "utf8")).version || null;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Compare two "x.y.z" strings. Returns >0 if a is newer than b.
|
|
22
|
+
export function cmpVersion(a, b) {
|
|
23
|
+
const pa = String(a).split(".").map(Number);
|
|
24
|
+
const pb = String(b).split(".").map(Number);
|
|
25
|
+
for (let i = 0; i < 3; i++) {
|
|
26
|
+
const d = (pa[i] || 0) - (pb[i] || 0);
|
|
27
|
+
if (d !== 0) return d;
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const COOLDOWN_FILE = join(tmpdir(), "aether-update-ts");
|
|
33
|
+
const COOLDOWN_MS = 3600_000; // 1 hour
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check for updates. If a newer version exists on npm, spawn a background
|
|
37
|
+
* `npm install -g` to auto-install it. Returns a one-line status message
|
|
38
|
+
* or null. Cooldown: only checks once per hour.
|
|
39
|
+
*/
|
|
40
|
+
export async function autoUpdate({ fetchImpl = fetch, timeoutMs = 2000 } = {}) {
|
|
41
|
+
const current = currentVersion();
|
|
42
|
+
if (!current) return null;
|
|
43
|
+
|
|
44
|
+
// Skip if we checked less than 1 hour ago
|
|
45
|
+
try {
|
|
46
|
+
const ts = parseInt(readFileSync(COOLDOWN_FILE, "utf8"), 10);
|
|
47
|
+
if (Date.now() - ts < COOLDOWN_MS) return null;
|
|
48
|
+
} catch { /* no file or parse error — check now */ }
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const ac = new AbortController();
|
|
52
|
+
const t = setTimeout(() => ac.abort(), timeoutMs);
|
|
53
|
+
let res;
|
|
54
|
+
try {
|
|
55
|
+
res = await fetchImpl("https://registry.npmjs.org/aether-code/latest", { signal: ac.signal });
|
|
56
|
+
} finally {
|
|
57
|
+
clearTimeout(t);
|
|
58
|
+
}
|
|
59
|
+
if (!res || !res.ok) return null;
|
|
60
|
+
const latest = (await res.json())?.version;
|
|
61
|
+
|
|
62
|
+
// Write cooldown regardless of whether an update is available
|
|
63
|
+
try { writeFileSync(COOLDOWN_FILE, String(Date.now())); } catch { /* ignore */ }
|
|
64
|
+
|
|
65
|
+
if (!latest || cmpVersion(latest, current) <= 0) return null;
|
|
66
|
+
|
|
67
|
+
// Spawn background npm install — detached so it doesn't block the CLI.
|
|
68
|
+
// On Windows, `npm` is a cmd script, so shell:true is required.
|
|
69
|
+
const child = spawn("npm", ["install", "-g", `aether-code@${latest}`], {
|
|
70
|
+
detached: true,
|
|
71
|
+
stdio: "ignore",
|
|
72
|
+
shell: true,
|
|
73
|
+
windowsHide: true,
|
|
74
|
+
});
|
|
75
|
+
child.unref();
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
c.cyan("↑ auto-updating ") +
|
|
79
|
+
c.gray(current) + c.gray(" → ") + c.bold(c.green(latest)) +
|
|
80
|
+
c.gray(" (background — applies next launch)")
|
|
81
|
+
);
|
|
82
|
+
} catch {
|
|
83
|
+
/* offline / aborted / parse error — stay silent */
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Legacy nudge-only check (kept for the test suite). Same as before: returns
|
|
90
|
+
* a one-line "update available" string or null.
|
|
91
|
+
*/
|
|
92
|
+
export async function checkForUpdate({ fetchImpl = fetch, timeoutMs = 2000 } = {}) {
|
|
93
|
+
const current = currentVersion();
|
|
94
|
+
if (!current) return null;
|
|
95
|
+
try {
|
|
96
|
+
const ac = new AbortController();
|
|
97
|
+
const t = setTimeout(() => ac.abort(), timeoutMs);
|
|
98
|
+
let res;
|
|
99
|
+
try {
|
|
100
|
+
res = await fetchImpl("https://registry.npmjs.org/aether-code/latest", { signal: ac.signal });
|
|
101
|
+
} finally {
|
|
102
|
+
clearTimeout(t);
|
|
103
|
+
}
|
|
104
|
+
if (!res || !res.ok) return null;
|
|
105
|
+
const latest = (await res.json())?.version;
|
|
106
|
+
if (latest && cmpVersion(latest, current) > 0) {
|
|
107
|
+
return (
|
|
108
|
+
c.yellow("update available: ") +
|
|
109
|
+
c.gray(current) + " -> " + c.bold(c.green(latest)) +
|
|
110
|
+
c.gray(" · run ") + c.cyan("npm i -g aether-code@latest")
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
/* offline / aborted / parse error — stay silent */
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|