nyte-ai 0.0.9-dev.1 → 0.0.15

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/docs/usage.md CHANGED
@@ -29,7 +29,7 @@ These requests use your configured provider and may incur charges. Run `nyte log
29
29
  interactively before using scripts. A script must also run in a trusted workspace.
30
30
 
31
31
  Use `nyte --resume` in a terminal to continue the latest conversation.
32
- Installed copies can update with `nyte update`. That command installs the new
32
+ Installed copies can update with `nyte update`. Run `nyte update --models` to refresh the model catalog without updating Nyte. The update command installs the new
33
33
  release's docs beside the executable when the release archive carries them.
34
34
 
35
35
  ## Delegated tasks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nyte-ai",
3
- "version": "0.0.9-dev.1",
3
+ "version": "0.0.15",
4
4
  "description": "Headless agent runner and terminal client for Nyte",
5
5
  "keywords": [
6
6
  "agent",
@@ -33,7 +33,7 @@
33
33
  "test": "vitest --run"
34
34
  },
35
35
  "devDependencies": {
36
- "vitest": "^5.0.0"
36
+ "vitest": "^5.0.2"
37
37
  },
38
38
  "engines": {
39
39
  "node": ">=26"
package/src/launcher.js CHANGED
@@ -5,12 +5,15 @@ import { homedir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
  import process from "node:process";
7
7
 
8
+ import packageJson from "../package.json" with { type: "json" };
9
+
8
10
  export const REPOSITORY = "interfaces-lab/nyte";
9
11
 
10
12
  export function platformTarget(platform = process.platform, arch = process.arch) {
11
13
  if ((platform !== "darwin" && platform !== "linux") || (arch !== "arm64" && arch !== "x64")) {
12
14
  return undefined;
13
15
  }
16
+
14
17
  return `${platform}-${arch}`;
15
18
  }
16
19
 
@@ -18,12 +21,6 @@ export function releaseAssetName(version, target) {
18
21
  return `nyte-v${version.replace(/^v/u, "")}-${target}`;
19
22
  }
20
23
 
21
- async function packageVersion() {
22
- const metadata = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
23
- if (typeof metadata.version !== "string") throw new Error("nyte-ai has no package version.");
24
- return metadata.version;
25
- }
26
-
27
24
  export function cacheRoot(env = process.env) {
28
25
  return env["NYTE_BIN_DIR"] ?? join(homedir(), ".nyte", "bin");
29
26
  }
@@ -31,12 +28,15 @@ export function cacheRoot(env = process.env) {
31
28
  async function sha256(path) {
32
29
  const hash = createHash("sha256");
33
30
  hash.update(await readFile(path));
31
+
34
32
  return hash.digest("hex");
35
33
  }
36
34
 
37
35
  async function fetchOk(fetchFn, url) {
38
36
  const response = await fetchFn(url, { signal: AbortSignal.timeout(10 * 60_000) });
37
+
39
38
  if (!response.ok) throw new Error(`Failed to download ${url} (HTTP ${response.status}).`);
39
+
40
40
  return response;
41
41
  }
42
42
 
@@ -46,13 +46,16 @@ async function download(fetchFn, url, path) {
46
46
  }
47
47
 
48
48
  export async function ensureBinary(options = {}) {
49
- const version = options.version ?? (await packageVersion());
49
+ const version = options.version ?? packageJson.version;
50
50
  const target = platformTarget(options.platform, options.arch);
51
+
51
52
  if (target === undefined) {
52
53
  throw new Error(`Nyte has no native release for ${process.platform}/${process.arch}.`);
53
54
  }
55
+
54
56
  const root = options.root ?? cacheRoot(options.env);
55
57
  const binary = join(root, version, "nyte");
58
+
56
59
  if ((await stat(binary).catch(() => undefined))?.isFile()) return binary;
57
60
 
58
61
  const fetchFn = options.fetchFn ?? globalThis.fetch;
@@ -61,20 +64,26 @@ export async function ensureBinary(options = {}) {
61
64
  const versionDir = dirname(binary);
62
65
  const archive = join(versionDir, `${asset}.tar.gz`);
63
66
  await mkdir(versionDir, { recursive: true });
67
+
64
68
  try {
65
69
  const checksum = (await (await fetchOk(fetchFn, `${base}/${asset}.tar.gz.sha256`)).text())
66
70
  .trim()
67
71
  .split(/\s+/u)[0]
68
72
  ?.toLowerCase();
73
+
69
74
  if (checksum === undefined || !/^[0-9a-f]{64}$/u.test(checksum)) {
70
75
  throw new Error(`The checksum file for ${asset} is malformed.`);
71
76
  }
77
+
72
78
  await download(fetchFn, `${base}/${asset}.tar.gz`, archive);
73
79
  const actual = await sha256(archive);
80
+
74
81
  if (actual !== checksum) {
75
82
  throw new Error(`Checksum mismatch for ${asset}: expected ${checksum}, got ${actual}.`);
76
83
  }
84
+
77
85
  const result = spawn("tar", ["-xzf", archive, "-C", versionDir], { stdio: "inherit" });
86
+
78
87
  const exitCode = await new Promise((resolve, reject) => {
79
88
  result.once("error", reject);
80
89
  result.once("exit", (code, signal) => {
@@ -82,10 +91,13 @@ export async function ensureBinary(options = {}) {
82
91
  else resolve(code);
83
92
  });
84
93
  });
94
+
85
95
  if (exitCode !== 0 || !(await stat(binary).catch(() => undefined))?.isFile()) {
86
96
  throw new Error(`${asset}.tar.gz does not contain a nyte binary.`);
87
97
  }
98
+
88
99
  await chmod(binary, 0o755);
100
+
89
101
  return binary;
90
102
  } catch (error) {
91
103
  await rm(binary, { force: true });
@@ -98,23 +110,30 @@ export async function ensureBinary(options = {}) {
98
110
  export async function launch(args = process.argv.slice(2), options = {}) {
99
111
  const binary = await ensureBinary(options);
100
112
  const child = spawn(binary, args, { stdio: "inherit" });
113
+
101
114
  const forwardInt = () => {
102
115
  if (!child.killed) child.kill("SIGINT");
103
116
  };
117
+
104
118
  const forwardTerm = () => {
105
119
  if (!child.killed) child.kill("SIGTERM");
106
120
  };
121
+
107
122
  process.once("SIGINT", forwardInt);
108
123
  process.once("SIGTERM", forwardTerm);
124
+
109
125
  return new Promise((resolve, reject) => {
110
126
  child.once("error", reject);
111
127
  child.once("exit", (code, signal) => {
112
128
  process.removeListener("SIGINT", forwardInt);
113
129
  process.removeListener("SIGTERM", forwardTerm);
130
+
114
131
  if (signal !== null) {
115
132
  process.kill(process.pid, signal);
133
+
116
134
  return;
117
135
  }
136
+
118
137
  resolve(code ?? 1);
119
138
  });
120
139
  });