@algosuite/vo-mcp 0.2.0-beta.8 → 0.2.0-beta.9
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/README.md +26 -2
- package/bin/vo-mcp +1 -0
- package/dist/cli.js +159 -0
- package/dist/cli.js.map +3 -3
- package/dist/index.js +158 -0
- package/dist/index.js.map +3 -3
- package/dist/install-cli.js +196 -54
- package/dist/install-cli.js.map +4 -4
- package/dist/runner-cli.js +24 -24
- package/dist/runner-cli.js.map +3 -3
- package/dist/runner-supervisor.js +71 -122
- package/dist/runner-supervisor.js.map +4 -4
- package/dist/supervisor-credential-helper.js +125 -0
- package/dist/supervisor-credential-helper.js.map +7 -0
- package/package.json +3 -2
package/dist/install-cli.js
CHANGED
|
@@ -3,8 +3,8 @@ import { createRequire as __cr } from 'module'; const require = __cr(import.meta
|
|
|
3
3
|
|
|
4
4
|
// src/install.ts
|
|
5
5
|
import { homedir as homedir3, platform as platform3 } from "node:os";
|
|
6
|
-
import { join as
|
|
7
|
-
import { existsSync as
|
|
6
|
+
import { join as join4, dirname as dirname3 } from "node:path";
|
|
7
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, copyFileSync as copyFileSync3 } from "node:fs";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
|
|
10
10
|
// src/cloud/pairing.ts
|
|
@@ -197,27 +197,156 @@ async function runPairing(deps = {}) {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
// src/codex-mcp-config.ts
|
|
201
|
+
import { copyFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
202
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
203
|
+
var MANAGED_BEGIN = "# BEGIN AlgoHQ MCP (managed by vo-mcp install)";
|
|
204
|
+
var MANAGED_END = "# END AlgoHQ MCP (managed by vo-mcp install)";
|
|
205
|
+
var MANAGED_SERVER_NAMES = /* @__PURE__ */ new Set(["algohq", "vo", "vo-mcp", "vo_mcp"]);
|
|
206
|
+
function resolveCodexConfigPath(home) {
|
|
207
|
+
return join2(home, ".codex", "config.toml");
|
|
208
|
+
}
|
|
209
|
+
function normalizeKey(value) {
|
|
210
|
+
const trimmed = value.trim();
|
|
211
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
212
|
+
return trimmed.slice(1, -1);
|
|
213
|
+
}
|
|
214
|
+
return trimmed;
|
|
215
|
+
}
|
|
216
|
+
function tablePath(line) {
|
|
217
|
+
const match = /^\s*\[([^\r\n]+)\]\s*(?:#.*)?$/.exec(line);
|
|
218
|
+
const rawPath = match?.[1];
|
|
219
|
+
if (!rawPath || rawPath.includes("[") || rawPath.includes("]")) return null;
|
|
220
|
+
return rawPath.split(".").map(normalizeKey);
|
|
221
|
+
}
|
|
222
|
+
function tableSections(lines) {
|
|
223
|
+
const starts = [];
|
|
224
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
225
|
+
const path = tablePath(lines[index] ?? "");
|
|
226
|
+
if (path) starts.push({ path, start: index });
|
|
227
|
+
}
|
|
228
|
+
return starts.map((section, index) => ({
|
|
229
|
+
...section,
|
|
230
|
+
end: starts[index + 1]?.start ?? lines.length
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
function isManagedSection(section) {
|
|
234
|
+
return section.path[0] === "mcp_servers" && MANAGED_SERVER_NAMES.has(section.path[1] ?? "");
|
|
235
|
+
}
|
|
236
|
+
function assignmentKey(line) {
|
|
237
|
+
const match = /^\s*((?:[A-Za-z0-9_-]+)|(?:"[^"]+")|(?:'[^']+'))\s*=/.exec(line);
|
|
238
|
+
return match?.[1] ? normalizeKey(match[1]) : null;
|
|
239
|
+
}
|
|
240
|
+
function isStructurallySafeToml(raw) {
|
|
241
|
+
const lines = raw.split(/\r?\n/);
|
|
242
|
+
const beginIndexes = lines.flatMap((line, index) => line.trim() === MANAGED_BEGIN ? [index] : []);
|
|
243
|
+
const endIndexes = lines.flatMap((line, index) => line.trim() === MANAGED_END ? [index] : []);
|
|
244
|
+
if (beginIndexes.length !== endIndexes.length || beginIndexes.length > 1) return false;
|
|
245
|
+
if (beginIndexes[0] !== void 0 && (endIndexes[0] ?? -1) <= beginIndexes[0]) return false;
|
|
246
|
+
for (const line of lines) {
|
|
247
|
+
const trimmed = line.trim();
|
|
248
|
+
if (/^\[\[?mcp_servers(?:\.|\s|$)/.test(trimmed) && tablePath(line) === null) return false;
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
function tomlString(value) {
|
|
253
|
+
return JSON.stringify(value);
|
|
254
|
+
}
|
|
255
|
+
function preservedSectionLines(lines, section, managedKeys) {
|
|
256
|
+
if (!section) return [];
|
|
257
|
+
return lines.slice(section.start + 1, section.end).filter((line) => {
|
|
258
|
+
if (line.trim() === MANAGED_BEGIN || line.trim() === MANAGED_END) return false;
|
|
259
|
+
const key = assignmentKey(line);
|
|
260
|
+
return key === null || !managedKeys.has(key);
|
|
261
|
+
}).filter((line, index, all) => line.trim() !== "" || index > 0 && index < all.length - 1);
|
|
262
|
+
}
|
|
263
|
+
function renderCodexMcpConfig(raw, cliPath, controlPlaneUrl) {
|
|
264
|
+
if (!isStructurallySafeToml(raw)) {
|
|
265
|
+
throw new Error("Codex config is malformed; refusing to overwrite it");
|
|
266
|
+
}
|
|
267
|
+
const eol = raw.includes("\r\n") ? "\r\n" : "\n";
|
|
268
|
+
const lines = raw.split(/\r?\n/);
|
|
269
|
+
const sections = tableSections(lines);
|
|
270
|
+
const rootSections = sections.filter((section) => isManagedSection(section) && section.path.length === 2);
|
|
271
|
+
const preferredRoot = rootSections.find((section) => section.path[1] === "algohq") ?? rootSections[0];
|
|
272
|
+
const preferredName = preferredRoot?.path[1];
|
|
273
|
+
const envSection = sections.find((section) => isManagedSection(section) && section.path[1] === preferredName && section.path[2] === "env");
|
|
274
|
+
const rootExtras = preservedSectionLines(lines, preferredRoot, /* @__PURE__ */ new Set(["command", "args", "required"]));
|
|
275
|
+
const envExtras = preservedSectionLines(lines, envSection, /* @__PURE__ */ new Set(["VO_CONTROL_PLANE_URL"]));
|
|
276
|
+
const removed = /* @__PURE__ */ new Set();
|
|
277
|
+
const managedBegin = lines.findIndex((line) => line.trim() === MANAGED_BEGIN);
|
|
278
|
+
const managedEnd = lines.findIndex((line) => line.trim() === MANAGED_END);
|
|
279
|
+
if (managedBegin >= 0 && managedEnd >= managedBegin) {
|
|
280
|
+
for (let index = managedBegin; index <= managedEnd; index += 1) removed.add(index);
|
|
281
|
+
}
|
|
282
|
+
for (const section of sections.filter(isManagedSection)) {
|
|
283
|
+
for (let index = section.start; index < section.end; index += 1) removed.add(index);
|
|
284
|
+
}
|
|
285
|
+
const base = lines.filter((_line, index) => !removed.has(index)).join(eol).trimEnd();
|
|
286
|
+
const block = [
|
|
287
|
+
MANAGED_BEGIN,
|
|
288
|
+
"[mcp_servers.algohq]",
|
|
289
|
+
'command = "node"',
|
|
290
|
+
`args = [${tomlString(cliPath)}]`,
|
|
291
|
+
"required = true",
|
|
292
|
+
...rootExtras,
|
|
293
|
+
"",
|
|
294
|
+
"[mcp_servers.algohq.env]",
|
|
295
|
+
`VO_CONTROL_PLANE_URL = ${tomlString(controlPlaneUrl)}`,
|
|
296
|
+
...envExtras,
|
|
297
|
+
MANAGED_END
|
|
298
|
+
].join(eol);
|
|
299
|
+
return `${base}${base ? `${eol}${eol}` : ""}${block}${eol}`;
|
|
300
|
+
}
|
|
301
|
+
function installCodexMcpConfigAt(configPath, cliPath, controlPlaneUrl, log) {
|
|
302
|
+
const exists = existsSync2(configPath);
|
|
303
|
+
const raw = exists ? readFileSync2(configPath, "utf8") : "";
|
|
304
|
+
let rendered;
|
|
305
|
+
try {
|
|
306
|
+
rendered = renderCodexMcpConfig(raw, cliPath, controlPlaneUrl);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (exists) {
|
|
309
|
+
const backupPath = `${configPath}.backup-${Date.now()}`;
|
|
310
|
+
copyFileSync(configPath, backupPath);
|
|
311
|
+
log(` Backed up malformed Codex config \u2192 ${backupPath}`);
|
|
312
|
+
}
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
if (rendered === raw) {
|
|
316
|
+
log(` Codex already current: ${configPath}`);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (exists) {
|
|
320
|
+
const backupPath = `${configPath}.backup-${Date.now()}`;
|
|
321
|
+
copyFileSync(configPath, backupPath);
|
|
322
|
+
log(` Backed up Codex config \u2192 ${backupPath}`);
|
|
323
|
+
}
|
|
324
|
+
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
325
|
+
writeFileSync2(configPath, rendered, "utf8");
|
|
326
|
+
log(`\u2713 Wrote AlgoHQ MCP to Codex: ${configPath}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
200
329
|
// src/autostart.ts
|
|
201
330
|
import { homedir as homedir2, platform as platform2 } from "node:os";
|
|
202
|
-
import { join as
|
|
203
|
-
import { existsSync as
|
|
331
|
+
import { join as join3 } from "node:path";
|
|
332
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync3, unlinkSync, copyFileSync as copyFileSync2 } from "node:fs";
|
|
204
333
|
function resolveRunnerCommand(override) {
|
|
205
334
|
return override ?? "vo-mcp runner";
|
|
206
335
|
}
|
|
207
336
|
function installWindowsAutostart(runnerCommand, log, env) {
|
|
208
|
-
const appData = env["APPDATA"] ??
|
|
209
|
-
const startupDir =
|
|
210
|
-
|
|
211
|
-
const launcherPath =
|
|
212
|
-
if (
|
|
213
|
-
const existing =
|
|
337
|
+
const appData = env["APPDATA"] ?? join3(homedir2(), "AppData", "Roaming");
|
|
338
|
+
const startupDir = join3(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
|
|
339
|
+
mkdirSync3(startupDir, { recursive: true });
|
|
340
|
+
const launcherPath = join3(startupDir, "vo-runner.cmd");
|
|
341
|
+
if (existsSync3(launcherPath)) {
|
|
342
|
+
const existing = readFileSync3(launcherPath, "utf8");
|
|
214
343
|
if (existing.includes("vo-mcp runner")) {
|
|
215
344
|
log(`\u2713 Auto-start is already configured (Windows Startup folder)`);
|
|
216
345
|
log(` Path: ${launcherPath}`);
|
|
217
346
|
return;
|
|
218
347
|
}
|
|
219
348
|
const backupPath = `${launcherPath}.backup-${Date.now()}`;
|
|
220
|
-
|
|
349
|
+
copyFileSync2(launcherPath, backupPath);
|
|
221
350
|
log(` Backed up existing launcher to: ${backupPath}`);
|
|
222
351
|
}
|
|
223
352
|
const launcherContent = `@echo off
|
|
@@ -225,24 +354,24 @@ REM Auto-start launcher for vo-mcp runner
|
|
|
225
354
|
REM Created by vo-mcp autostart installer
|
|
226
355
|
start /min cmd /c "${runnerCommand}"
|
|
227
356
|
`;
|
|
228
|
-
|
|
357
|
+
writeFileSync3(launcherPath, launcherContent, "utf8");
|
|
229
358
|
log(`\u2713 Installed Windows auto-start launcher`);
|
|
230
359
|
log(` Path: ${launcherPath}`);
|
|
231
360
|
log(` The runner will start minimized at next login.`);
|
|
232
361
|
}
|
|
233
362
|
async function installMacAutostart(runnerCommand, log) {
|
|
234
|
-
const launchAgentsDir =
|
|
235
|
-
|
|
236
|
-
const plistPath =
|
|
237
|
-
if (
|
|
238
|
-
const existing =
|
|
363
|
+
const launchAgentsDir = join3(homedir2(), "Library", "LaunchAgents");
|
|
364
|
+
mkdirSync3(launchAgentsDir, { recursive: true });
|
|
365
|
+
const plistPath = join3(launchAgentsDir, "ai.algosuite.vo-runner.plist");
|
|
366
|
+
if (existsSync3(plistPath)) {
|
|
367
|
+
const existing = readFileSync3(plistPath, "utf8");
|
|
239
368
|
if (existing.includes("vo-mcp runner")) {
|
|
240
369
|
log(`\u2713 Auto-start is already configured (launchd)`);
|
|
241
370
|
log(` Path: ${plistPath}`);
|
|
242
371
|
return;
|
|
243
372
|
}
|
|
244
373
|
const backupPath = `${plistPath}.backup-${Date.now()}`;
|
|
245
|
-
|
|
374
|
+
copyFileSync2(plistPath, backupPath);
|
|
246
375
|
log(` Backed up existing plist to: ${backupPath}`);
|
|
247
376
|
}
|
|
248
377
|
const parts = runnerCommand.split(/\s+/);
|
|
@@ -264,20 +393,20 @@ ${args.map((a) => ` <string>${a}</string>`).join("\n")}
|
|
|
264
393
|
<key>KeepAlive</key>
|
|
265
394
|
<true/>
|
|
266
395
|
<key>StandardOutPath</key>
|
|
267
|
-
<string>${
|
|
396
|
+
<string>${join3(homedir2(), ".claude", "vo-runner.log")}</string>
|
|
268
397
|
<key>StandardErrorPath</key>
|
|
269
|
-
<string>${
|
|
398
|
+
<string>${join3(homedir2(), ".claude", "vo-runner-error.log")}</string>
|
|
270
399
|
</dict>
|
|
271
400
|
</plist>
|
|
272
401
|
`;
|
|
273
|
-
|
|
402
|
+
writeFileSync3(plistPath, plistContent, "utf8");
|
|
274
403
|
log(`\u2713 Installed launchd plist`);
|
|
275
404
|
log(` Path: ${plistPath}`);
|
|
276
405
|
try {
|
|
277
406
|
const { execSync } = await import("node:child_process");
|
|
278
407
|
execSync(`launchctl load "${plistPath}"`, { stdio: "ignore" });
|
|
279
408
|
log(`\u2713 Loaded plist with launchctl (runner will start at next login)`);
|
|
280
|
-
log(` Logs: ${
|
|
409
|
+
log(` Logs: ${join3(homedir2(), ".claude", "vo-runner.log")}`);
|
|
281
410
|
} catch {
|
|
282
411
|
log(`\u26A0 Failed to load plist with launchctl (you may need to load it manually)`);
|
|
283
412
|
log(` Run: launchctl load "${plistPath}"`);
|
|
@@ -285,23 +414,23 @@ ${args.map((a) => ` <string>${a}</string>`).join("\n")}
|
|
|
285
414
|
}
|
|
286
415
|
async function installLinuxAutostart(runnerCommand, log, env) {
|
|
287
416
|
const home = env["HOME"]?.trim() || homedir2();
|
|
288
|
-
const unitDir =
|
|
289
|
-
|
|
290
|
-
const unitPath =
|
|
291
|
-
if (
|
|
292
|
-
const existing =
|
|
417
|
+
const unitDir = join3(home, ".config", "systemd", "user");
|
|
418
|
+
mkdirSync3(unitDir, { recursive: true });
|
|
419
|
+
const unitPath = join3(unitDir, "vo-runner.service");
|
|
420
|
+
if (existsSync3(unitPath)) {
|
|
421
|
+
const existing = readFileSync3(unitPath, "utf8");
|
|
293
422
|
if (existing.includes(runnerCommand) || existing.includes("vo-mcp runner")) {
|
|
294
423
|
log(`\u2713 Auto-start is already configured (systemd user unit)`);
|
|
295
424
|
log(` Path: ${unitPath}`);
|
|
296
425
|
return;
|
|
297
426
|
}
|
|
298
427
|
const backupPath = `${unitPath}.backup-${Date.now()}`;
|
|
299
|
-
|
|
428
|
+
copyFileSync2(unitPath, backupPath);
|
|
300
429
|
log(` Backed up existing unit to: ${backupPath}`);
|
|
301
430
|
}
|
|
302
|
-
const logFile =
|
|
303
|
-
const errFile =
|
|
304
|
-
|
|
431
|
+
const logFile = join3(home, ".claude", "vo-runner.log");
|
|
432
|
+
const errFile = join3(home, ".claude", "vo-runner-error.log");
|
|
433
|
+
mkdirSync3(join3(home, ".claude"), { recursive: true });
|
|
305
434
|
const unit = `[Unit]
|
|
306
435
|
Description=VO Code Runner (vo-mcp)
|
|
307
436
|
After=network-online.target
|
|
@@ -318,7 +447,7 @@ StandardError=append:${errFile}
|
|
|
318
447
|
[Install]
|
|
319
448
|
WantedBy=default.target
|
|
320
449
|
`;
|
|
321
|
-
|
|
450
|
+
writeFileSync3(unitPath, unit, "utf8");
|
|
322
451
|
log(`\u2713 Installed systemd user unit`);
|
|
323
452
|
log(` Path: ${unitPath}`);
|
|
324
453
|
if (process.env["VITEST"]) {
|
|
@@ -356,21 +485,21 @@ async function installAutostart(opts = {}) {
|
|
|
356
485
|
// src/install.ts
|
|
357
486
|
var DEFAULT_CONTROL_PLANE_URL2 = "https://vo-control-plane-bzjphrajaq-uc.a.run.app";
|
|
358
487
|
function resolveCodeConfigPath(home) {
|
|
359
|
-
return
|
|
488
|
+
return join4(home, ".claude.json");
|
|
360
489
|
}
|
|
361
490
|
function resolveDesktopConfigPath(home, plat, appData) {
|
|
362
491
|
if (plat === "win32") {
|
|
363
|
-
return
|
|
492
|
+
return join4(appData ?? join4(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
|
|
364
493
|
}
|
|
365
494
|
if (plat === "darwin") {
|
|
366
|
-
return
|
|
495
|
+
return join4(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
367
496
|
}
|
|
368
|
-
return
|
|
497
|
+
return join4(home, ".config", "Claude", "claude_desktop_config.json");
|
|
369
498
|
}
|
|
370
499
|
function readClaudeConfig(path) {
|
|
371
500
|
try {
|
|
372
|
-
if (!
|
|
373
|
-
const raw =
|
|
501
|
+
if (!existsSync4(path)) return {};
|
|
502
|
+
const raw = readFileSync4(path, "utf8");
|
|
374
503
|
const parsed = JSON.parse(raw);
|
|
375
504
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
376
505
|
} catch {
|
|
@@ -378,12 +507,12 @@ function readClaudeConfig(path) {
|
|
|
378
507
|
}
|
|
379
508
|
}
|
|
380
509
|
function writeClaudeConfig(path, config) {
|
|
381
|
-
|
|
382
|
-
|
|
510
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
511
|
+
writeFileSync4(path, `${JSON.stringify(config, null, 2)}
|
|
383
512
|
`, "utf8");
|
|
384
513
|
}
|
|
385
514
|
function resolveVoMcpCliPath() {
|
|
386
|
-
return
|
|
515
|
+
return join4(dirname3(fileURLToPath(import.meta.url)), "cli.js");
|
|
387
516
|
}
|
|
388
517
|
function installMcpConfigAt(configPath, cliPath, controlPlaneUrl, log, label) {
|
|
389
518
|
const existing = readClaudeConfig(configPath);
|
|
@@ -393,9 +522,9 @@ function installMcpConfigAt(configPath, cliPath, controlPlaneUrl, log, label) {
|
|
|
393
522
|
log(` ${label} already current: ${configPath}`);
|
|
394
523
|
return;
|
|
395
524
|
}
|
|
396
|
-
if (
|
|
525
|
+
if (existsSync4(configPath)) {
|
|
397
526
|
const backupPath = `${configPath}.backup-${Date.now()}`;
|
|
398
|
-
|
|
527
|
+
copyFileSync3(configPath, backupPath);
|
|
399
528
|
log(` Backed up ${label} config \u2192 ${backupPath}`);
|
|
400
529
|
}
|
|
401
530
|
const merged = {
|
|
@@ -422,6 +551,7 @@ function installMcpConfig(log, env) {
|
|
|
422
551
|
const controlPlaneUrl = env["VO_CONTROL_PLANE_URL"]?.trim() || DEFAULT_CONTROL_PLANE_URL2;
|
|
423
552
|
installMcpConfigAt(resolveCodeConfigPath(home), cliPath, controlPlaneUrl, log, "Claude Code CLI");
|
|
424
553
|
installMcpConfigAt(resolveDesktopConfigPath(home, plat, appData), cliPath, controlPlaneUrl, log, "Claude Desktop");
|
|
554
|
+
installCodexMcpConfigAt(resolveCodexConfigPath(home), cliPath, controlPlaneUrl, log);
|
|
425
555
|
}
|
|
426
556
|
async function runPairFlow(log, env) {
|
|
427
557
|
log("\n\u2501\u2501\u2501 Step 2: Link your VO account (device code) \u2501\u2501\u2501");
|
|
@@ -437,27 +567,33 @@ async function runPairFlow(log, env) {
|
|
|
437
567
|
log(" No problem \u2014 the rest will finish; pair anytime with: vo-mcp pair");
|
|
438
568
|
}
|
|
439
569
|
}
|
|
440
|
-
function printNextSteps(log, autostartInstalled) {
|
|
570
|
+
function printNextSteps(log, autostartInstalled, configOnly) {
|
|
441
571
|
log("\n\u2501\u2501\u2501 Installation complete! \u2501\u2501\u2501\n");
|
|
442
572
|
log("What's configured:");
|
|
443
|
-
log(" \u2713 Claude Desktop
|
|
444
|
-
|
|
573
|
+
log(" \u2713 Claude Desktop, Claude Code, and Codex will load AlgoHQ MCP on next restart");
|
|
574
|
+
if (configOnly) {
|
|
575
|
+
log(" \u2713 Existing pairing and runner auto-start settings were left unchanged");
|
|
576
|
+
} else {
|
|
577
|
+
log(" \u2713 Your scoped credential is stored (revocable via the dashboard)");
|
|
578
|
+
}
|
|
445
579
|
if (autostartInstalled) {
|
|
446
580
|
log(" \u2713 Runner daemon will start automatically at login\n");
|
|
447
581
|
} else {
|
|
448
582
|
log("\n");
|
|
449
583
|
}
|
|
450
584
|
log("Next steps:");
|
|
451
|
-
log(" 1. Restart Claude Desktop / Claude Code (if running).");
|
|
452
|
-
if (
|
|
585
|
+
log(" 1. Restart Claude Desktop / Claude Code / Codex (if running).");
|
|
586
|
+
if (configOnly) {
|
|
587
|
+
log(" 2. Restart the existing AlgoHQ runner service or runner terminal.");
|
|
588
|
+
} else if (autostartInstalled) {
|
|
453
589
|
log(" 2. Log out and back in (or start the runner manually now: vo-mcp runner)");
|
|
454
590
|
} else {
|
|
455
591
|
log(" 2. Start the agent runner in a terminal (keep it running):");
|
|
456
592
|
log(" vo-mcp runner");
|
|
457
593
|
log(" (To set up auto-start at login: vo-mcp runner --install-autostart)");
|
|
458
594
|
}
|
|
459
|
-
log(" 3. Visit
|
|
460
|
-
log(" https://algosuite.ai/
|
|
595
|
+
log(" 3. Visit AlgoHQ to dispatch your first agent:");
|
|
596
|
+
log(" https://algosuite.ai/algohq\n");
|
|
461
597
|
log("The runner watches for tasks you dispatch and spins up agents in fresh worktrees.");
|
|
462
598
|
log("Agents only run while the runner is connected. Ctrl+C to stop it anytime.\n");
|
|
463
599
|
}
|
|
@@ -465,11 +601,13 @@ async function install(opts = {}) {
|
|
|
465
601
|
const log = opts.log ?? ((m) => console.error(m));
|
|
466
602
|
const env = opts.env ?? process.env;
|
|
467
603
|
log("\u2501\u2501\u2501 vo-mcp installer \u2501\u2501\u2501");
|
|
468
|
-
log("This will set up your machine to dispatch
|
|
469
|
-
log("\u2501\u2501\u2501 Step 1: Configure Claude Desktop / Claude Code \u2501\u2501\u2501");
|
|
604
|
+
log("This will set up your machine to dispatch AlgoHQ agents from anywhere.\n");
|
|
605
|
+
log("\u2501\u2501\u2501 Step 1: Configure Claude Desktop / Claude Code / Codex \u2501\u2501\u2501");
|
|
470
606
|
installMcpConfig(log, env);
|
|
471
607
|
if (!opts.skipLogin) {
|
|
472
608
|
await runPairFlow(log, env);
|
|
609
|
+
} else if (opts.configOnly) {
|
|
610
|
+
log("\n(Config-only refresh \u2014 existing pairing left unchanged.)");
|
|
473
611
|
} else {
|
|
474
612
|
log("\n(Pairing skipped \u2014 run `vo-mcp pair` when ready.)");
|
|
475
613
|
}
|
|
@@ -484,11 +622,15 @@ async function install(opts = {}) {
|
|
|
484
622
|
autostartInstalled = true;
|
|
485
623
|
}
|
|
486
624
|
}
|
|
487
|
-
printNextSteps(log, autostartInstalled);
|
|
625
|
+
printNextSteps(log, autostartInstalled, opts.configOnly === true);
|
|
626
|
+
}
|
|
627
|
+
function installOptionsFromArgs(args) {
|
|
628
|
+
const configOnly = args.includes("--config-only");
|
|
629
|
+
return configOnly ? { configOnly: true, skipLogin: true, skipAutostart: true } : {};
|
|
488
630
|
}
|
|
489
631
|
|
|
490
632
|
// src/install-cli.ts
|
|
491
|
-
install().catch((err) => {
|
|
633
|
+
install(installOptionsFromArgs(process.argv.slice(2))).catch((err) => {
|
|
492
634
|
console.error("[vo-mcp install] fatal:", err);
|
|
493
635
|
process.exit(1);
|
|
494
636
|
});
|