athena-agent-launcher 0.3.0 → 0.3.2

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.
Files changed (2) hide show
  1. package/bin/launcher.mjs +73 -18
  2. package/package.json +1 -1
package/bin/launcher.mjs CHANGED
@@ -139,42 +139,97 @@ async function runSetup() {
139
139
  process.stdout.write(" Run an agent with: athena-agent run <agent-id>\n\n");
140
140
  }
141
141
 
142
- // Return the absolute path to `hermes` if it's findable on PATH, in
143
- // ~/.local/bin, or in a few other common locations. Returns null if not found.
142
+ // Return the absolute path to `hermes` if it's findable on PATH or in a few
143
+ // common install locations. Cross-platform (uses `where` on Windows, `which`
144
+ // elsewhere) and never throws if the finder binary is missing. Returns null if
145
+ // not found.
144
146
  async function locateHermes() {
145
- const userLocalBin = join(homedir(), ".local", "bin", "hermes");
146
- const candidates = [userLocalBin, "/usr/local/bin/hermes", "/opt/homebrew/bin/hermes"];
147
+ const isWin = process.platform === "win32";
148
+ const home = homedir();
149
+ const candidates = isWin
150
+ ? [
151
+ join(home, ".local", "bin", "hermes.exe"),
152
+ join(home, ".local", "bin", "hermes.cmd"),
153
+ join(home, ".local", "bin", "hermes"),
154
+ ]
155
+ : [join(home, ".local", "bin", "hermes"), "/usr/local/bin/hermes", "/opt/homebrew/bin/hermes"];
147
156
  for (const p of candidates) {
148
157
  if (existsSync(p)) return p;
149
158
  }
150
- const which = await new Promise((resolve) => {
151
- const c = spawn("which", ["hermes"], { stdio: ["ignore", "pipe", "ignore"] });
159
+ // `where` (Windows) / `which` (POSIX). Wrapped so a missing finder binary
160
+ // surfaces as null instead of an unhandled 'error' event (the ENOENT crash).
161
+ const finder = isWin ? "where" : "which";
162
+ return await new Promise((resolve) => {
152
163
  let out = "";
153
- c.stdout.on("data", (d) => (out += d.toString()));
154
- c.on("exit", () => resolve(out.trim()));
164
+ let child;
165
+ try {
166
+ child = spawn(finder, ["hermes"], { stdio: ["ignore", "pipe", "ignore"] });
167
+ } catch {
168
+ return resolve(null);
169
+ }
170
+ child.on("error", () => resolve(null));
171
+ child.stdout.on("data", (d) => (out += d.toString()));
172
+ child.on("exit", () => {
173
+ const first = out
174
+ .split(/\r?\n/)
175
+ .map((s) => s.trim())
176
+ .filter(Boolean)[0];
177
+ resolve(first || null);
178
+ });
155
179
  });
156
- return which || null;
157
180
  }
158
181
 
159
182
  async function ensureHermesInstalled() {
160
183
  const found = await locateHermes();
161
184
  if (found) return found;
162
185
 
186
+ const isWin = process.platform === "win32";
163
187
  process.stderr.write(
164
- "[athena-agent] Hermes is not installed. Installing silently (this is a one-time setup)…\n",
188
+ "[athena-agent] Hermes is not installed. Installing (this is a one-time setup)…\n",
165
189
  );
166
- // `bash -s -- --skip-setup` passes the flag to the install script and
167
- // suppresses its post-install wizard, so the user only ever sees Athena's UX.
168
190
  const code = await new Promise((resolve) => {
169
- const c = spawn(
170
- "bash",
171
- ["-c", "curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --skip-setup"],
172
- { stdio: "inherit" },
173
- );
191
+ let c;
192
+ try {
193
+ c = isWin
194
+ ? // Native Windows installer (PowerShell). Hermes' CLI + ACP run
195
+ // natively; only its own dashboard pane needs WSL2, which we don't use.
196
+ // `-SkipSetup` suppresses the interactive wizard (Nous Portal login,
197
+ // model/backend prompts) — Athena overwrites Hermes' config to point at
198
+ // the Athena proxy anyway, so the wizard is pure friction. iex can't
199
+ // pass params, so build a scriptblock from the downloaded script.
200
+ spawn(
201
+ "powershell",
202
+ [
203
+ "-NoProfile",
204
+ "-ExecutionPolicy",
205
+ "Bypass",
206
+ "-Command",
207
+ "& ([scriptblock]::Create((irm https://hermes-agent.nousresearch.com/install.ps1))) -SkipSetup",
208
+ ],
209
+ { stdio: "inherit" },
210
+ )
211
+ : // `bash -s -- --skip-setup` suppresses the installer's wizard so the
212
+ // user only ever sees Athena's UX.
213
+ spawn(
214
+ "bash",
215
+ [
216
+ "-c",
217
+ "curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --skip-setup",
218
+ ],
219
+ { stdio: "inherit" },
220
+ );
221
+ } catch {
222
+ return resolve(1);
223
+ }
224
+ c.on("error", () => resolve(1));
174
225
  c.on("exit", (code) => resolve(code ?? 1));
175
226
  });
176
227
  if (code !== 0) {
177
- process.stderr.write("[athena-agent] Hermes install failed. See output above.\n");
228
+ process.stderr.write(
229
+ "[athena-agent] Hermes install failed. On Windows you can also install it manually\n" +
230
+ " (PowerShell): iex (irm https://hermes-agent.nousresearch.com/install.ps1)\n" +
231
+ " then re-run this command. Or use WSL2 for the fully-supported Linux path.\n",
232
+ );
178
233
  return null;
179
234
  }
180
235
  process.stderr.write("[athena-agent] Hermes installed.\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "athena-agent-launcher",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Run an Athena-configured agent locally. Resolves runtime + bindings from the Athena control plane and spawns the correct agent binary (Anthropic SDK, OpenClaw, or Hermes).",
5
5
  "type": "module",
6
6
  "bin": {