nexus-agents 2.61.0 → 2.63.0
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/dist/{chunk-TRWIEUI2.js → chunk-4G7MSCIK.js} +2 -2
- package/dist/{chunk-KNAPTURC.js → chunk-7C32M23X.js} +2 -2
- package/dist/{chunk-NB6IYTMN.js → chunk-FMFQJLMR.js} +3 -3
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +464 -47
- package/dist/cli.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{setup-command-QJGB34JY.js → setup-command-QKAVRVLV.js} +3 -3
- package/package.json +1 -1
- /package/dist/{chunk-TRWIEUI2.js.map → chunk-4G7MSCIK.js.map} +0 -0
- /package/dist/{chunk-KNAPTURC.js.map → chunk-7C32M23X.js.map} +0 -0
- /package/dist/{chunk-NB6IYTMN.js.map → chunk-FMFQJLMR.js.map} +0 -0
- /package/dist/{setup-command-QJGB34JY.js.map → setup-command-QKAVRVLV.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import "./chunk-6QU4DJYW.js";
|
|
|
16
16
|
import {
|
|
17
17
|
setupCommandAsync,
|
|
18
18
|
verifyCommand
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-7C32M23X.js";
|
|
20
20
|
import "./chunk-QGZBCD2A.js";
|
|
21
21
|
import {
|
|
22
22
|
AuthHandler,
|
|
@@ -142,7 +142,7 @@ import {
|
|
|
142
142
|
validateCommand,
|
|
143
143
|
validateWorkflow,
|
|
144
144
|
wrapInMarkdownFence
|
|
145
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-4G7MSCIK.js";
|
|
146
146
|
import "./chunk-ED6VQWNG.js";
|
|
147
147
|
import {
|
|
148
148
|
resolveToken
|
|
@@ -201,7 +201,7 @@ import {
|
|
|
201
201
|
loadConfig,
|
|
202
202
|
runDoctor,
|
|
203
203
|
validateNexusEnv
|
|
204
|
-
} from "./chunk-
|
|
204
|
+
} from "./chunk-FMFQJLMR.js";
|
|
205
205
|
import {
|
|
206
206
|
DEFAULTS
|
|
207
207
|
} from "./chunk-H43PABG4.js";
|
|
@@ -17663,6 +17663,20 @@ var PARSE_ARGS_CONFIG = {
|
|
|
17663
17663
|
gitignore: {
|
|
17664
17664
|
type: "boolean",
|
|
17665
17665
|
default: false
|
|
17666
|
+
},
|
|
17667
|
+
// init --portable --mcp-config flag (#2308)
|
|
17668
|
+
"mcp-config": {
|
|
17669
|
+
type: "boolean",
|
|
17670
|
+
default: false
|
|
17671
|
+
},
|
|
17672
|
+
// init --portable --install / --uninstall flags (#2311)
|
|
17673
|
+
install: {
|
|
17674
|
+
type: "boolean",
|
|
17675
|
+
default: false
|
|
17676
|
+
},
|
|
17677
|
+
uninstall: {
|
|
17678
|
+
type: "boolean",
|
|
17679
|
+
default: false
|
|
17666
17680
|
}
|
|
17667
17681
|
},
|
|
17668
17682
|
allowPositionals: true,
|
|
@@ -17721,18 +17735,294 @@ function isValidCommand(value) {
|
|
|
17721
17735
|
}
|
|
17722
17736
|
|
|
17723
17737
|
// src/cli-commands-handlers.ts
|
|
17724
|
-
import { existsSync as
|
|
17738
|
+
import { existsSync as existsSync22 } from "fs";
|
|
17725
17739
|
|
|
17726
17740
|
// src/cli/init-portable.ts
|
|
17727
17741
|
import {
|
|
17728
|
-
existsSync as
|
|
17729
|
-
mkdirSync as
|
|
17742
|
+
existsSync as existsSync20,
|
|
17743
|
+
mkdirSync as mkdirSync5,
|
|
17730
17744
|
readdirSync as readdirSync2,
|
|
17731
17745
|
statSync as statSync3,
|
|
17732
|
-
appendFileSync,
|
|
17733
|
-
readFileSync as
|
|
17746
|
+
appendFileSync as appendFileSync2,
|
|
17747
|
+
readFileSync as readFileSync12
|
|
17734
17748
|
} from "fs";
|
|
17735
|
-
import { resolve as resolve11, join as
|
|
17749
|
+
import { resolve as resolve11, join as join16, isAbsolute as isAbsolute2 } from "path";
|
|
17750
|
+
|
|
17751
|
+
// src/cli/mcp-config-emitter.ts
|
|
17752
|
+
import { existsSync as existsSync17, readFileSync as readFileSync10, writeFileSync as writeFileSync5, appendFileSync } from "fs";
|
|
17753
|
+
import { join as join13 } from "path";
|
|
17754
|
+
var MCP_CONFIG_FILENAME = ".mcp.json";
|
|
17755
|
+
var NEXUS_SERVER_KEY = "nexus-agents";
|
|
17756
|
+
function buildNexusServerEntry(dataDir, commandPath) {
|
|
17757
|
+
return {
|
|
17758
|
+
command: commandPath ?? "nexus-agents",
|
|
17759
|
+
args: ["--mode=server"],
|
|
17760
|
+
env: { NEXUS_DATA_DIR: dataDir }
|
|
17761
|
+
};
|
|
17762
|
+
}
|
|
17763
|
+
function entriesEqual(a, b) {
|
|
17764
|
+
if (a.command !== b.command) return false;
|
|
17765
|
+
if (a.args.length !== b.args.length) return false;
|
|
17766
|
+
for (let i = 0; i < a.args.length; i++) if (a.args[i] !== b.args[i]) return false;
|
|
17767
|
+
const aEnv = a.env ?? {};
|
|
17768
|
+
const bEnv = b.env ?? {};
|
|
17769
|
+
const aKeys = Object.keys(aEnv);
|
|
17770
|
+
const bKeys = Object.keys(bEnv);
|
|
17771
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
17772
|
+
for (const k of aKeys) if (aEnv[k] !== bEnv[k]) return false;
|
|
17773
|
+
return true;
|
|
17774
|
+
}
|
|
17775
|
+
function loadExistingConfig(path23) {
|
|
17776
|
+
if (!existsSync17(path23)) return { ok: true, value: void 0 };
|
|
17777
|
+
let raw;
|
|
17778
|
+
try {
|
|
17779
|
+
raw = readFileSync10(path23, "utf-8");
|
|
17780
|
+
} catch (e) {
|
|
17781
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
17782
|
+
}
|
|
17783
|
+
try {
|
|
17784
|
+
const parsed = JSON.parse(raw);
|
|
17785
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
17786
|
+
return { ok: false, error: `${path23}: top-level JSON must be an object` };
|
|
17787
|
+
}
|
|
17788
|
+
return { ok: true, value: parsed };
|
|
17789
|
+
} catch (e) {
|
|
17790
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
17791
|
+
return { ok: false, error: `${path23}: invalid JSON \u2014 ${msg}` };
|
|
17792
|
+
}
|
|
17793
|
+
}
|
|
17794
|
+
function decideEmission(existing, desired, force) {
|
|
17795
|
+
if (existing === void 0) {
|
|
17796
|
+
return { kind: "write", nextConfig: { mcpServers: { [NEXUS_SERVER_KEY]: desired } } };
|
|
17797
|
+
}
|
|
17798
|
+
const servers = existing.mcpServers ?? {};
|
|
17799
|
+
const current = servers[NEXUS_SERVER_KEY];
|
|
17800
|
+
if (current !== void 0 && entriesEqual(current, desired)) {
|
|
17801
|
+
return { kind: "noop" };
|
|
17802
|
+
}
|
|
17803
|
+
if (current !== void 0 && !force) {
|
|
17804
|
+
return {
|
|
17805
|
+
kind: "refuse",
|
|
17806
|
+
reason: `existing ${NEXUS_SERVER_KEY} entry differs; pass --force to overwrite`
|
|
17807
|
+
};
|
|
17808
|
+
}
|
|
17809
|
+
const nextServers = { ...servers, [NEXUS_SERVER_KEY]: desired };
|
|
17810
|
+
return { kind: "write", nextConfig: { ...existing, mcpServers: nextServers } };
|
|
17811
|
+
}
|
|
17812
|
+
function autoGitignoreMcpConfig(workspaceDir, dryRun) {
|
|
17813
|
+
const gitDir = join13(workspaceDir, ".git");
|
|
17814
|
+
if (!existsSync17(gitDir)) return false;
|
|
17815
|
+
const gitignorePath = join13(workspaceDir, ".gitignore");
|
|
17816
|
+
let existing = "";
|
|
17817
|
+
if (existsSync17(gitignorePath)) {
|
|
17818
|
+
existing = readFileSync10(gitignorePath, "utf-8");
|
|
17819
|
+
const already = existing.split("\n").some((l) => l.trim() === MCP_CONFIG_FILENAME || l.trim() === `/${MCP_CONFIG_FILENAME}`);
|
|
17820
|
+
if (already) return false;
|
|
17821
|
+
}
|
|
17822
|
+
if (!dryRun) {
|
|
17823
|
+
const sep3 = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
17824
|
+
appendFileSync(gitignorePath, `${sep3}${MCP_CONFIG_FILENAME}
|
|
17825
|
+
`, "utf-8");
|
|
17826
|
+
}
|
|
17827
|
+
return true;
|
|
17828
|
+
}
|
|
17829
|
+
function emitMcpConfig(options) {
|
|
17830
|
+
const mcpConfigPath = join13(options.workspaceDir, MCP_CONFIG_FILENAME);
|
|
17831
|
+
const dryRun = options.dryRun === true;
|
|
17832
|
+
const force = options.force === true;
|
|
17833
|
+
const desired = buildNexusServerEntry(options.dataDir, options.commandPath);
|
|
17834
|
+
const loaded = loadExistingConfig(mcpConfigPath);
|
|
17835
|
+
if (!loaded.ok) return makeFailure(mcpConfigPath, loaded.error);
|
|
17836
|
+
const decision = decideEmission(loaded.value, desired, force);
|
|
17837
|
+
if (decision.kind === "refuse") return makeFailure(mcpConfigPath, decision.reason);
|
|
17838
|
+
if (decision.kind === "noop") {
|
|
17839
|
+
return makeSuccess({
|
|
17840
|
+
mcpConfigPath,
|
|
17841
|
+
written: false,
|
|
17842
|
+
alreadyMatched: true,
|
|
17843
|
+
gitignoreUpdated: false
|
|
17844
|
+
});
|
|
17845
|
+
}
|
|
17846
|
+
if (!dryRun) {
|
|
17847
|
+
writeFileSync5(mcpConfigPath, JSON.stringify(decision.nextConfig, null, 2) + "\n", "utf-8");
|
|
17848
|
+
}
|
|
17849
|
+
const gitignoreUpdated = autoGitignoreMcpConfig(options.workspaceDir, dryRun);
|
|
17850
|
+
return makeSuccess({ mcpConfigPath, written: true, alreadyMatched: false, gitignoreUpdated });
|
|
17851
|
+
}
|
|
17852
|
+
function makeSuccess(opts) {
|
|
17853
|
+
return { success: true, error: null, ...opts };
|
|
17854
|
+
}
|
|
17855
|
+
function makeFailure(mcpConfigPath, error) {
|
|
17856
|
+
return {
|
|
17857
|
+
success: false,
|
|
17858
|
+
mcpConfigPath,
|
|
17859
|
+
written: false,
|
|
17860
|
+
alreadyMatched: false,
|
|
17861
|
+
gitignoreUpdated: false,
|
|
17862
|
+
error
|
|
17863
|
+
};
|
|
17864
|
+
}
|
|
17865
|
+
|
|
17866
|
+
// src/cli/portable-installer.ts
|
|
17867
|
+
import { execFile } from "child_process";
|
|
17868
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync4, rmSync, writeFileSync as writeFileSync7 } from "fs";
|
|
17869
|
+
import { join as join15 } from "path";
|
|
17870
|
+
import { promisify } from "util";
|
|
17871
|
+
|
|
17872
|
+
// src/cli/bin-shim.ts
|
|
17873
|
+
import { existsSync as existsSync18, readFileSync as readFileSync11, writeFileSync as writeFileSync6, chmodSync as chmodSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
17874
|
+
import { join as join14 } from "path";
|
|
17875
|
+
var SHIM_BASENAME = "nexus-agents";
|
|
17876
|
+
var SHIM_MODE = 493;
|
|
17877
|
+
function buildShimContents(cliEntryPath) {
|
|
17878
|
+
const lines = [
|
|
17879
|
+
"#!/usr/bin/env node",
|
|
17880
|
+
"// Generated by `nexus-agents init --portable --install` (#3a).",
|
|
17881
|
+
"// Do not edit by hand \u2014 re-run `init --portable --install` to refresh.",
|
|
17882
|
+
`import('${cliEntryPath}').catch((e) => { console.error(e); process.exit(1); });`,
|
|
17883
|
+
""
|
|
17884
|
+
];
|
|
17885
|
+
return lines.join("\n");
|
|
17886
|
+
}
|
|
17887
|
+
function writeBinShim(options) {
|
|
17888
|
+
const shimPath = join14(options.binDir, SHIM_BASENAME);
|
|
17889
|
+
const desired = buildShimContents(options.cliEntryPath);
|
|
17890
|
+
const dryRun = options.dryRun === true;
|
|
17891
|
+
try {
|
|
17892
|
+
if (existsSync18(shimPath)) {
|
|
17893
|
+
const current = readFileSync11(shimPath, "utf-8");
|
|
17894
|
+
if (current === desired) {
|
|
17895
|
+
return { success: true, shimPath, written: false, alreadyMatched: true, error: null };
|
|
17896
|
+
}
|
|
17897
|
+
}
|
|
17898
|
+
if (!dryRun) {
|
|
17899
|
+
if (!existsSync18(options.binDir)) mkdirSync3(options.binDir, { recursive: true });
|
|
17900
|
+
writeFileSync6(shimPath, desired, "utf-8");
|
|
17901
|
+
chmodSync2(shimPath, SHIM_MODE);
|
|
17902
|
+
}
|
|
17903
|
+
return { success: true, shimPath, written: true, alreadyMatched: false, error: null };
|
|
17904
|
+
} catch (error) {
|
|
17905
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
17906
|
+
return { success: false, shimPath, written: false, alreadyMatched: false, error: msg };
|
|
17907
|
+
}
|
|
17908
|
+
}
|
|
17909
|
+
|
|
17910
|
+
// src/cli/portable-installer.ts
|
|
17911
|
+
var execFileAsync = promisify(execFile);
|
|
17912
|
+
var CLI_SUBDIR = "cli";
|
|
17913
|
+
var BIN_SUBDIR = "bin";
|
|
17914
|
+
var CLI_ENTRY_RELATIVE = "node_modules/nexus-agents/dist/cli.js";
|
|
17915
|
+
var NPM_INSTALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
17916
|
+
function resolveInstallVersion(override) {
|
|
17917
|
+
const v = override ?? VERSION;
|
|
17918
|
+
if (v === "dev" || v === "" || v.includes(" ")) {
|
|
17919
|
+
return {
|
|
17920
|
+
ok: false,
|
|
17921
|
+
error: `cannot resolve install version: got '${v}'. Portable install requires a published nexus-agents version (build the CLI from a release).`
|
|
17922
|
+
};
|
|
17923
|
+
}
|
|
17924
|
+
return { ok: true, value: v };
|
|
17925
|
+
}
|
|
17926
|
+
function writeInstallManifest(cliDir, version) {
|
|
17927
|
+
const manifest = {
|
|
17928
|
+
name: "nexus-agents-portable-shim",
|
|
17929
|
+
private: true,
|
|
17930
|
+
version: "0.0.0",
|
|
17931
|
+
description: "Local install root for portable nexus-agents (generated; do not commit)",
|
|
17932
|
+
dependencies: { "nexus-agents": version }
|
|
17933
|
+
};
|
|
17934
|
+
writeFileSync7(join15(cliDir, "package.json"), JSON.stringify(manifest, null, 2) + "\n", "utf-8");
|
|
17935
|
+
}
|
|
17936
|
+
function isAlreadyInstalled(cliDir) {
|
|
17937
|
+
return existsSync19(join15(cliDir, "node_modules", "nexus-agents", "package.json"));
|
|
17938
|
+
}
|
|
17939
|
+
async function spawnNpmInstall(cliDir) {
|
|
17940
|
+
await execFileAsync("npm", ["install", "--no-audit", "--no-fund", "--silent"], {
|
|
17941
|
+
cwd: cliDir,
|
|
17942
|
+
timeout: NPM_INSTALL_TIMEOUT_MS
|
|
17943
|
+
});
|
|
17944
|
+
}
|
|
17945
|
+
function cleanupOnFailure(cliDir) {
|
|
17946
|
+
try {
|
|
17947
|
+
rmSync(cliDir, { recursive: true, force: true });
|
|
17948
|
+
} catch {
|
|
17949
|
+
}
|
|
17950
|
+
}
|
|
17951
|
+
async function runNpmStep(ctx) {
|
|
17952
|
+
try {
|
|
17953
|
+
if (!existsSync19(ctx.cliDir)) mkdirSync4(ctx.cliDir, { recursive: true });
|
|
17954
|
+
writeInstallManifest(ctx.cliDir, ctx.version);
|
|
17955
|
+
await spawnNpmInstall(ctx.cliDir);
|
|
17956
|
+
return void 0;
|
|
17957
|
+
} catch (error) {
|
|
17958
|
+
cleanupOnFailure(ctx.cliDir);
|
|
17959
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
17960
|
+
return { success: false, ...ctx, skipped: false, error: `npm install failed: ${msg}` };
|
|
17961
|
+
}
|
|
17962
|
+
}
|
|
17963
|
+
async function installPortable(options) {
|
|
17964
|
+
const cliDir = join15(options.dataDir, CLI_SUBDIR);
|
|
17965
|
+
const binDir = join15(options.dataDir, BIN_SUBDIR);
|
|
17966
|
+
const versionResolution = resolveInstallVersion(options.version);
|
|
17967
|
+
if (!versionResolution.ok) {
|
|
17968
|
+
return {
|
|
17969
|
+
success: false,
|
|
17970
|
+
version: "",
|
|
17971
|
+
cliDir,
|
|
17972
|
+
binDir,
|
|
17973
|
+
skipped: false,
|
|
17974
|
+
error: versionResolution.error
|
|
17975
|
+
};
|
|
17976
|
+
}
|
|
17977
|
+
const ctx = { cliDir, binDir, version: versionResolution.value };
|
|
17978
|
+
if (isAlreadyInstalled(cliDir) && options.force !== true) {
|
|
17979
|
+
return { success: true, ...ctx, skipped: true, error: null };
|
|
17980
|
+
}
|
|
17981
|
+
if (options.dryRun === true) {
|
|
17982
|
+
return { success: true, ...ctx, skipped: false, error: null };
|
|
17983
|
+
}
|
|
17984
|
+
const npmFailure = await runNpmStep(ctx);
|
|
17985
|
+
if (npmFailure !== void 0) return npmFailure;
|
|
17986
|
+
const shim = writeBinShim({ binDir, cliEntryPath: join15(cliDir, CLI_ENTRY_RELATIVE) });
|
|
17987
|
+
if (!shim.success) {
|
|
17988
|
+
cleanupOnFailure(cliDir);
|
|
17989
|
+
return {
|
|
17990
|
+
success: false,
|
|
17991
|
+
...ctx,
|
|
17992
|
+
shim,
|
|
17993
|
+
skipped: false,
|
|
17994
|
+
error: `bin shim emission failed: ${shim.error ?? "unknown"}`
|
|
17995
|
+
};
|
|
17996
|
+
}
|
|
17997
|
+
return { success: true, ...ctx, shim, skipped: false, error: null };
|
|
17998
|
+
}
|
|
17999
|
+
function uninstallPortable(options) {
|
|
18000
|
+
const cliDir = join15(options.dataDir, CLI_SUBDIR);
|
|
18001
|
+
const binDir = join15(options.dataDir, BIN_SUBDIR);
|
|
18002
|
+
const removed = [];
|
|
18003
|
+
const notPresent = [];
|
|
18004
|
+
const dryRun = options.dryRun === true;
|
|
18005
|
+
try {
|
|
18006
|
+
for (const dir of [cliDir, binDir]) {
|
|
18007
|
+
if (!existsSync19(dir)) {
|
|
18008
|
+
notPresent.push(dir);
|
|
18009
|
+
continue;
|
|
18010
|
+
}
|
|
18011
|
+
if (!dryRun) rmSync(dir, { recursive: true, force: true });
|
|
18012
|
+
removed.push(dir);
|
|
18013
|
+
}
|
|
18014
|
+
return { success: true, removed, notPresent, error: null };
|
|
18015
|
+
} catch (error) {
|
|
18016
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
18017
|
+
return { success: false, removed, notPresent, error: msg };
|
|
18018
|
+
}
|
|
18019
|
+
}
|
|
18020
|
+
function findBinShim(dataDir) {
|
|
18021
|
+
const shimPath = join15(dataDir, BIN_SUBDIR, "nexus-agents");
|
|
18022
|
+
return existsSync19(shimPath) ? shimPath : void 0;
|
|
18023
|
+
}
|
|
18024
|
+
|
|
18025
|
+
// src/cli/init-portable.ts
|
|
17736
18026
|
var DEFAULT_PORTABLE_DIRNAME = ".nexus-agents";
|
|
17737
18027
|
var RESTRICTED_SUBDIRS = /* @__PURE__ */ new Set(["auth"]);
|
|
17738
18028
|
function resolveTargetPath(rawPath) {
|
|
@@ -17742,53 +18032,53 @@ function resolveTargetPath(rawPath) {
|
|
|
17742
18032
|
return isAbsolute2(rawPath) ? rawPath : resolve11(process.cwd(), rawPath);
|
|
17743
18033
|
}
|
|
17744
18034
|
function isNonEmpty(dir) {
|
|
17745
|
-
if (!
|
|
18035
|
+
if (!existsSync20(dir)) return false;
|
|
17746
18036
|
const stat2 = statSync3(dir);
|
|
17747
18037
|
if (!stat2.isDirectory()) return true;
|
|
17748
18038
|
return readdirSync2(dir).length > 0;
|
|
17749
18039
|
}
|
|
17750
18040
|
function ensureDir(path23, dryRun, created, alreadyExisted, mode) {
|
|
17751
|
-
if (
|
|
18041
|
+
if (existsSync20(path23)) {
|
|
17752
18042
|
alreadyExisted.push(path23);
|
|
17753
18043
|
return;
|
|
17754
18044
|
}
|
|
17755
18045
|
if (!dryRun) {
|
|
17756
|
-
|
|
18046
|
+
mkdirSync5(path23, { recursive: true, ...mode !== void 0 ? { mode } : {} });
|
|
17757
18047
|
}
|
|
17758
18048
|
created.push(path23);
|
|
17759
18049
|
}
|
|
17760
18050
|
function maybeUpdateGitignore(workspaceDir, portableDirName, dryRun) {
|
|
17761
|
-
const gitDir =
|
|
17762
|
-
if (!
|
|
17763
|
-
const gitignorePath =
|
|
18051
|
+
const gitDir = join16(workspaceDir, ".git");
|
|
18052
|
+
if (!existsSync20(gitDir)) return false;
|
|
18053
|
+
const gitignorePath = join16(workspaceDir, ".gitignore");
|
|
17764
18054
|
const entry = `${portableDirName}/`;
|
|
17765
18055
|
let existing = "";
|
|
17766
|
-
if (
|
|
17767
|
-
existing =
|
|
18056
|
+
if (existsSync20(gitignorePath)) {
|
|
18057
|
+
existing = readFileSync12(gitignorePath, "utf-8");
|
|
17768
18058
|
if (existing.split("\n").some((l) => l.trim() === entry || l.trim() === portableDirName)) {
|
|
17769
18059
|
return false;
|
|
17770
18060
|
}
|
|
17771
18061
|
}
|
|
17772
18062
|
if (!dryRun) {
|
|
17773
18063
|
const sep3 = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
17774
|
-
|
|
18064
|
+
appendFileSync2(gitignorePath, `${sep3}${entry}
|
|
17775
18065
|
`, "utf-8");
|
|
17776
18066
|
}
|
|
17777
18067
|
return true;
|
|
17778
18068
|
}
|
|
17779
18069
|
function inspectTarget(target) {
|
|
17780
|
-
const exists =
|
|
18070
|
+
const exists = existsSync20(target);
|
|
17781
18071
|
if (!exists) return { exists: false, nonEmpty: false, isExistingNexusDir: false };
|
|
17782
18072
|
const nonEmpty = isNonEmpty(target);
|
|
17783
18073
|
const stat2 = statSync3(target);
|
|
17784
|
-
const isExistingNexusDir = stat2.isDirectory() &&
|
|
18074
|
+
const isExistingNexusDir = stat2.isDirectory() && existsSync20(join16(target, "audit"));
|
|
17785
18075
|
return { exists, nonEmpty, isExistingNexusDir };
|
|
17786
18076
|
}
|
|
17787
18077
|
function createDataLayout(target, dryRun, created, alreadyExisted) {
|
|
17788
18078
|
ensureDir(target, dryRun, created, alreadyExisted);
|
|
17789
18079
|
for (const subdir of DATA_SUBDIRECTORIES) {
|
|
17790
18080
|
const mode = RESTRICTED_SUBDIRS.has(subdir) ? 448 : void 0;
|
|
17791
|
-
ensureDir(
|
|
18081
|
+
ensureDir(join16(target, subdir), dryRun, created, alreadyExisted, mode);
|
|
17792
18082
|
}
|
|
17793
18083
|
}
|
|
17794
18084
|
function makeResult(opts) {
|
|
@@ -17799,6 +18089,9 @@ function makeResult(opts) {
|
|
|
17799
18089
|
alreadyExisted: opts.alreadyExisted,
|
|
17800
18090
|
skipped: opts.skipped ?? false,
|
|
17801
18091
|
gitignoreUpdated: opts.gitignoreUpdated ?? false,
|
|
18092
|
+
...opts.mcpConfig !== void 0 ? { mcpConfig: opts.mcpConfig } : {},
|
|
18093
|
+
...opts.install !== void 0 ? { install: opts.install } : {},
|
|
18094
|
+
...opts.uninstall !== void 0 ? { uninstall: opts.uninstall } : {},
|
|
17802
18095
|
error: opts.error ?? null
|
|
17803
18096
|
};
|
|
17804
18097
|
}
|
|
@@ -17808,7 +18101,63 @@ function applyGitignoreOption(target, options, dryRun) {
|
|
|
17808
18101
|
const portableName = target.slice(workspaceDir.length + 1);
|
|
17809
18102
|
return maybeUpdateGitignore(workspaceDir, portableName, dryRun);
|
|
17810
18103
|
}
|
|
17811
|
-
function
|
|
18104
|
+
function applyMcpConfigOption(target, options, dryRun) {
|
|
18105
|
+
if (options.mcpConfig !== true) return void 0;
|
|
18106
|
+
const workspaceDir = resolve11(target, "..");
|
|
18107
|
+
const shimPath = findBinShim(target);
|
|
18108
|
+
return emitMcpConfig({
|
|
18109
|
+
workspaceDir,
|
|
18110
|
+
dataDir: target,
|
|
18111
|
+
...shimPath !== void 0 && { commandPath: shimPath },
|
|
18112
|
+
force: options.force === true,
|
|
18113
|
+
dryRun
|
|
18114
|
+
});
|
|
18115
|
+
}
|
|
18116
|
+
async function applyInstallOption(target, options, dryRun) {
|
|
18117
|
+
if (options.install !== true) return void 0;
|
|
18118
|
+
return installPortable({ dataDir: target, force: options.force === true, dryRun });
|
|
18119
|
+
}
|
|
18120
|
+
function buildSuccessResult(base, flags, extras) {
|
|
18121
|
+
const installFailed = extras.install !== void 0 && !extras.install.success;
|
|
18122
|
+
const mcpFailed = extras.mcpConfig !== void 0 && !extras.mcpConfig.success;
|
|
18123
|
+
if (installFailed) {
|
|
18124
|
+
return makeResult({
|
|
18125
|
+
...base,
|
|
18126
|
+
...flags,
|
|
18127
|
+
success: false,
|
|
18128
|
+
...extras,
|
|
18129
|
+
error: extras.install?.error ?? "install failed"
|
|
18130
|
+
});
|
|
18131
|
+
}
|
|
18132
|
+
if (mcpFailed) {
|
|
18133
|
+
return makeResult({
|
|
18134
|
+
...base,
|
|
18135
|
+
...flags,
|
|
18136
|
+
success: false,
|
|
18137
|
+
...extras,
|
|
18138
|
+
error: extras.mcpConfig?.error ?? "mcp-config emission failed"
|
|
18139
|
+
});
|
|
18140
|
+
}
|
|
18141
|
+
return makeResult({ ...base, ...flags, success: true, ...extras });
|
|
18142
|
+
}
|
|
18143
|
+
async function collectExtras(target, options, dryRun) {
|
|
18144
|
+
const extras = {};
|
|
18145
|
+
const install = await applyInstallOption(target, options, dryRun);
|
|
18146
|
+
if (install !== void 0) extras.install = install;
|
|
18147
|
+
const mcpConfig = applyMcpConfigOption(target, options, dryRun);
|
|
18148
|
+
if (mcpConfig !== void 0) extras.mcpConfig = mcpConfig;
|
|
18149
|
+
return extras;
|
|
18150
|
+
}
|
|
18151
|
+
function handleUninstall(target, base, dryRun) {
|
|
18152
|
+
const uninstall = uninstallPortable({ dataDir: target, dryRun });
|
|
18153
|
+
return makeResult({
|
|
18154
|
+
...base,
|
|
18155
|
+
success: uninstall.success,
|
|
18156
|
+
uninstall,
|
|
18157
|
+
error: uninstall.error
|
|
18158
|
+
});
|
|
18159
|
+
}
|
|
18160
|
+
async function initPortable(options = {}) {
|
|
17812
18161
|
const created = [];
|
|
17813
18162
|
const alreadyExisted = [];
|
|
17814
18163
|
const dryRun = options.dryRun === true;
|
|
@@ -17816,10 +18165,12 @@ function initPortable(options = {}) {
|
|
|
17816
18165
|
const target = resolveTargetPath(options.path);
|
|
17817
18166
|
const base = { absolutePath: target, created, alreadyExisted };
|
|
17818
18167
|
try {
|
|
18168
|
+
if (options.uninstall === true) return handleUninstall(target, base, dryRun);
|
|
17819
18169
|
const state = inspectTarget(target);
|
|
17820
18170
|
if (state.isExistingNexusDir && !force) {
|
|
17821
18171
|
createDataLayout(target, dryRun, created, alreadyExisted);
|
|
17822
|
-
|
|
18172
|
+
const extras2 = await collectExtras(target, options, dryRun);
|
|
18173
|
+
return buildSuccessResult(base, { skipped: true }, extras2);
|
|
17823
18174
|
}
|
|
17824
18175
|
if (state.nonEmpty && !state.isExistingNexusDir && !force) {
|
|
17825
18176
|
const error = `target ${target} already exists and is not empty; pass --force to use anyway`;
|
|
@@ -17827,37 +18178,83 @@ function initPortable(options = {}) {
|
|
|
17827
18178
|
}
|
|
17828
18179
|
createDataLayout(target, dryRun, created, alreadyExisted);
|
|
17829
18180
|
const gitignoreUpdated = applyGitignoreOption(target, options, dryRun);
|
|
17830
|
-
|
|
18181
|
+
const extras = await collectExtras(target, options, dryRun);
|
|
18182
|
+
return buildSuccessResult(base, { gitignoreUpdated }, extras);
|
|
17831
18183
|
} catch (error) {
|
|
17832
18184
|
const msg = error instanceof Error ? error.message : String(error);
|
|
17833
18185
|
return makeResult({ ...base, success: false, error: msg });
|
|
17834
18186
|
}
|
|
17835
18187
|
}
|
|
18188
|
+
function renderMcpConfigLines(mcpConfig) {
|
|
18189
|
+
const lines = [];
|
|
18190
|
+
if (mcpConfig.alreadyMatched) {
|
|
18191
|
+
lines.push(`\u2713 .mcp.json already up to date: ${mcpConfig.mcpConfigPath}`);
|
|
18192
|
+
} else if (mcpConfig.written) {
|
|
18193
|
+
lines.push(`\u2713 Wrote MCP config: ${mcpConfig.mcpConfigPath}`);
|
|
18194
|
+
}
|
|
18195
|
+
if (mcpConfig.gitignoreUpdated) {
|
|
18196
|
+
lines.push(`\u2713 Added .mcp.json to .gitignore (per-machine; do not commit)`);
|
|
18197
|
+
}
|
|
18198
|
+
return lines;
|
|
18199
|
+
}
|
|
18200
|
+
function renderMcpConfigCaveat(mcpConfig) {
|
|
18201
|
+
if (mcpConfig?.written !== true) return [];
|
|
18202
|
+
return [
|
|
18203
|
+
"",
|
|
18204
|
+
"Note: .mcp.json contains an absolute path to your local data dir.",
|
|
18205
|
+
"It is per-machine and should NOT be committed \u2014 collaborators should",
|
|
18206
|
+
"run `nexus-agents init --portable --mcp-config` themselves."
|
|
18207
|
+
];
|
|
18208
|
+
}
|
|
18209
|
+
function renderInstallLines(install) {
|
|
18210
|
+
if (install.skipped) return [`\u2713 Portable install already present (${install.version})`];
|
|
18211
|
+
return [
|
|
18212
|
+
`\u2713 Installed nexus-agents@${install.version} \u2192 ${install.cliDir}`,
|
|
18213
|
+
`\u2713 Wrote bin shim \u2192 ${install.shim?.shimPath ?? install.binDir + "/nexus-agents"}`
|
|
18214
|
+
];
|
|
18215
|
+
}
|
|
18216
|
+
function renderUninstallLines(uninstall) {
|
|
18217
|
+
const lines = [];
|
|
18218
|
+
if (uninstall.removed.length === 0 && uninstall.notPresent.length > 0) {
|
|
18219
|
+
lines.push("Nothing to uninstall \u2014 cli/ and bin/ were not present.");
|
|
18220
|
+
}
|
|
18221
|
+
for (const r of uninstall.removed) lines.push(`\u2713 Removed: ${r}`);
|
|
18222
|
+
if (uninstall.removed.length > 0) {
|
|
18223
|
+
lines.push("");
|
|
18224
|
+
lines.push("Note: data subdirs (memory, audit, voting, sessions, \u2026) preserved.");
|
|
18225
|
+
lines.push("To purge data too, remove the parent dir manually.");
|
|
18226
|
+
}
|
|
18227
|
+
return lines;
|
|
18228
|
+
}
|
|
17836
18229
|
function formatInitPortableMessage(result, dryRun) {
|
|
17837
18230
|
if (!result.success) {
|
|
17838
18231
|
return `init --portable failed: ${result.error ?? "unknown error"}
|
|
17839
18232
|
`;
|
|
17840
18233
|
}
|
|
17841
|
-
|
|
17842
|
-
|
|
17843
|
-
lines.push(`(dry-run) would create ${String(result.created.length)} entries under:`);
|
|
17844
|
-
lines.push(` ${result.absolutePath}`);
|
|
17845
|
-
return lines.join("\n") + "\n";
|
|
17846
|
-
}
|
|
17847
|
-
if (result.skipped) {
|
|
17848
|
-
lines.push(`\u2713 Already initialized: ${result.absolutePath}`);
|
|
17849
|
-
} else {
|
|
17850
|
-
lines.push(`\u2713 Created: ${result.absolutePath}`);
|
|
18234
|
+
if (result.uninstall !== void 0) {
|
|
18235
|
+
return renderUninstallLines(result.uninstall).join("\n") + "\n";
|
|
17851
18236
|
}
|
|
17852
|
-
if (
|
|
17853
|
-
|
|
18237
|
+
if (dryRun) {
|
|
18238
|
+
const lines2 = [
|
|
18239
|
+
`(dry-run) would create ${String(result.created.length)} entries under:`,
|
|
18240
|
+
` ${result.absolutePath}`
|
|
18241
|
+
];
|
|
18242
|
+
return lines2.join("\n") + "\n";
|
|
17854
18243
|
}
|
|
18244
|
+
const lines = [];
|
|
18245
|
+
lines.push(
|
|
18246
|
+
result.skipped ? `\u2713 Already initialized: ${result.absolutePath}` : `\u2713 Created: ${result.absolutePath}`
|
|
18247
|
+
);
|
|
18248
|
+
if (result.gitignoreUpdated) lines.push(`\u2713 Added entry to .gitignore`);
|
|
18249
|
+
if (result.install !== void 0) lines.push(...renderInstallLines(result.install));
|
|
18250
|
+
if (result.mcpConfig !== void 0) lines.push(...renderMcpConfigLines(result.mcpConfig));
|
|
17855
18251
|
lines.push("");
|
|
17856
18252
|
lines.push("Activate by exporting:");
|
|
17857
18253
|
lines.push(` export NEXUS_DATA_DIR=${result.absolutePath}`);
|
|
17858
18254
|
lines.push("");
|
|
17859
18255
|
lines.push("Or one-off:");
|
|
17860
18256
|
lines.push(` NEXUS_DATA_DIR=${result.absolutePath} nexus-agents <cmd>`);
|
|
18257
|
+
lines.push(...renderMcpConfigCaveat(result.mcpConfig));
|
|
17861
18258
|
return lines.join("\n") + "\n";
|
|
17862
18259
|
}
|
|
17863
18260
|
|
|
@@ -20672,8 +21069,8 @@ function printFirstRunHint() {
|
|
|
20672
21069
|
const isTTY = process.stderr.isTTY;
|
|
20673
21070
|
if (!isTTY) return;
|
|
20674
21071
|
const dataDir = getNexusDataDir();
|
|
20675
|
-
const hasConfig =
|
|
20676
|
-
if (
|
|
21072
|
+
const hasConfig = existsSync22("./nexus-agents.yaml") || existsSync22("./nexus-agents.yml");
|
|
21073
|
+
if (existsSync22(dataDir) || hasConfig) return;
|
|
20677
21074
|
process.stderr.write(
|
|
20678
21075
|
"\n\x1B[36mnexus-agents\x1B[0m: First time? Run \x1B[1mnexus-agents setup\x1B[0m to configure.\n\n"
|
|
20679
21076
|
);
|
|
@@ -20839,19 +21236,29 @@ async function handleDoctorCommand(args) {
|
|
|
20839
21236
|
}
|
|
20840
21237
|
process.exit(exitCode === 0 ? EXIT_CODES.SUCCESS : EXIT_CODES.SERVER_START_FAILED);
|
|
20841
21238
|
}
|
|
20842
|
-
function
|
|
21239
|
+
function validateInitFlags(args) {
|
|
20843
21240
|
if (args.options.portable !== true) {
|
|
20844
21241
|
process.stderr.write(
|
|
20845
|
-
"Usage: nexus-agents init --portable [path] [--force] [--dry-run]
|
|
21242
|
+
"Usage: nexus-agents init --portable [path] [--force] [--dry-run]\n [--gitignore] [--mcp-config]\n [--install | --uninstall]\nBootstraps a workspace-local nexus-agents data directory.\n"
|
|
20846
21243
|
);
|
|
20847
21244
|
process.exit(EXIT_CODES.INVALID_ARGS);
|
|
20848
21245
|
}
|
|
21246
|
+
if (args.options.install === true && args.options.uninstall === true) {
|
|
21247
|
+
process.stderr.write("Error: --install and --uninstall are mutually exclusive.\n");
|
|
21248
|
+
process.exit(EXIT_CODES.INVALID_ARGS);
|
|
21249
|
+
}
|
|
21250
|
+
}
|
|
21251
|
+
async function handleInitCommand(args) {
|
|
21252
|
+
validateInitFlags(args);
|
|
20849
21253
|
const targetPath = args.positionals[1];
|
|
20850
|
-
const result = initPortable({
|
|
21254
|
+
const result = await initPortable({
|
|
20851
21255
|
...targetPath !== void 0 && targetPath !== "" ? { path: targetPath } : {},
|
|
20852
21256
|
force: args.options.force,
|
|
20853
21257
|
dryRun: args.options.dryRun,
|
|
20854
|
-
gitignore: args.options.gitignore ?? false
|
|
21258
|
+
gitignore: args.options.gitignore ?? false,
|
|
21259
|
+
mcpConfig: args.options.mcpConfig ?? false,
|
|
21260
|
+
install: args.options.install ?? false,
|
|
21261
|
+
uninstall: args.options.uninstall ?? false
|
|
20855
21262
|
});
|
|
20856
21263
|
process.stdout.write(formatInitPortableMessage(result, args.options.dryRun));
|
|
20857
21264
|
process.exit(result.success ? EXIT_CODES.SUCCESS : EXIT_CODES.SERVER_START_FAILED);
|
|
@@ -22329,7 +22736,7 @@ function handleStatusCommand2(args) {
|
|
|
22329
22736
|
|
|
22330
22737
|
// src/cli/scenario-command.ts
|
|
22331
22738
|
import { readdir as readdir4 } from "fs/promises";
|
|
22332
|
-
import { join as
|
|
22739
|
+
import { join as join18, resolve as resolve14 } from "path";
|
|
22333
22740
|
|
|
22334
22741
|
// src/testing/e2e/scenario-runner.ts
|
|
22335
22742
|
import { readFile as readFile7 } from "fs/promises";
|
|
@@ -22699,7 +23106,7 @@ async function handleRun(args) {
|
|
|
22699
23106
|
process.exit(EXIT_CODES.SERVER_START_FAILED);
|
|
22700
23107
|
}
|
|
22701
23108
|
const runner = createScenarioRunner();
|
|
22702
|
-
const fixturePath =
|
|
23109
|
+
const fixturePath = join18(FIXTURES_DIR, `${name}${SCENARIO_SUFFIX}`);
|
|
22703
23110
|
try {
|
|
22704
23111
|
const fixture = await runner.loadFixture(fixturePath);
|
|
22705
23112
|
const result = await runner.run(fixture);
|
|
@@ -22962,8 +23369,6 @@ var SYNC_COMMAND_HANDLERS = {
|
|
|
22962
23369
|
status: handleStatusCommand2,
|
|
22963
23370
|
// Issue #1023: Warm-Up Command
|
|
22964
23371
|
"warm-up": handleWarmUpCommand,
|
|
22965
|
-
// #2305: Init Portable Command
|
|
22966
|
-
init: handleInitCommand,
|
|
22967
23372
|
"e2e-eval": handleE2EEvalCommand,
|
|
22968
23373
|
"routing-ab": handleRoutingABCommand,
|
|
22969
23374
|
"memory-eval": handleMemoryEvalCommand,
|
|
@@ -23003,6 +23408,8 @@ var ASYNC_COMMAND_HANDLERS = {
|
|
|
23003
23408
|
hooks: handleHooksCommand,
|
|
23004
23409
|
setup: handleSetupCommandAsync,
|
|
23005
23410
|
// Uses async for interactive wizard support (Issue #425)
|
|
23411
|
+
// #2305 / #2308 / #2311: Init Portable Command (async because --install spawns npm)
|
|
23412
|
+
init: handleInitCommand,
|
|
23006
23413
|
demo: handleDemoCommand,
|
|
23007
23414
|
// Made async for live CLI execution
|
|
23008
23415
|
// Issue #526: Newly wired async commands
|
|
@@ -23410,7 +23817,17 @@ function buildOptions(values) {
|
|
|
23410
23817
|
...buildSweBenchOptions(values),
|
|
23411
23818
|
...buildAtbenchOptions(values),
|
|
23412
23819
|
...buildLearningMetricsOptions(values),
|
|
23413
|
-
...buildSetupOptions(values)
|
|
23820
|
+
...buildSetupOptions(values),
|
|
23821
|
+
...buildInitOptions(values)
|
|
23822
|
+
};
|
|
23823
|
+
}
|
|
23824
|
+
function buildInitOptions(values) {
|
|
23825
|
+
return {
|
|
23826
|
+
portable: values.portable,
|
|
23827
|
+
gitignore: values.gitignore,
|
|
23828
|
+
mcpConfig: values["mcp-config"],
|
|
23829
|
+
install: values.install,
|
|
23830
|
+
uninstall: values.uninstall
|
|
23414
23831
|
};
|
|
23415
23832
|
}
|
|
23416
23833
|
function parseCliArgs(args = process.argv.slice(2)) {
|