@phnx-labs/agents-cli 1.20.67 → 1.20.68
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/CHANGELOG.md +55 -0
- package/README.md +3 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/check.js +96 -2
- package/dist/commands/doctor.js +37 -9
- package/dist/commands/exec.js +39 -1
- package/dist/commands/plugins.js +11 -2
- package/dist/commands/repo.js +47 -2
- package/dist/commands/ssh.js +103 -2
- package/dist/commands/teams.d.ts +11 -0
- package/dist/commands/teams.js +39 -1
- package/dist/commands/view.d.ts +1 -0
- package/dist/commands/view.js +12 -8
- package/dist/lib/agents.d.ts +11 -5
- package/dist/lib/agents.js +27 -24
- package/dist/lib/browser/profiles.js +4 -25
- package/dist/lib/cli-entry.d.ts +23 -0
- package/dist/lib/cli-entry.js +116 -0
- package/dist/lib/daemon.d.ts +2 -2
- package/dist/lib/daemon.js +8 -89
- package/dist/lib/devices/fleet.d.ts +23 -0
- package/dist/lib/devices/fleet.js +38 -0
- package/dist/lib/devices/health-report.d.ts +46 -0
- package/dist/lib/devices/health-report.js +159 -0
- package/dist/lib/devices/health.d.ts +14 -4
- package/dist/lib/devices/health.js +49 -8
- package/dist/lib/git.d.ts +10 -0
- package/dist/lib/git.js +23 -0
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/ready.js +5 -3
- package/dist/lib/hosts/remote-cmd.d.ts +12 -0
- package/dist/lib/hosts/remote-cmd.js +17 -1
- package/dist/lib/mcp.d.ts +21 -0
- package/dist/lib/mcp.js +278 -13
- package/dist/lib/resources/mcp.js +20 -342
- package/dist/lib/routines.d.ts +13 -0
- package/dist/lib/routines.js +21 -0
- package/dist/lib/runner.js +18 -40
- package/dist/lib/secrets/agent.js +11 -15
- package/dist/lib/share/capture.js +21 -3
- package/dist/lib/signin-badge.d.ts +41 -0
- package/dist/lib/signin-badge.js +64 -0
- package/dist/lib/ssh-exec.d.ts +5 -0
- package/dist/lib/ssh-exec.js +55 -1
- package/dist/lib/usage.d.ts +17 -3
- package/dist/lib/usage.js +63 -16
- package/package.json +1 -1
package/dist/lib/mcp.js
CHANGED
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
import * as fs from 'fs';
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import * as yaml from 'yaml';
|
|
13
|
+
import * as TOML from 'smol-toml';
|
|
13
14
|
import { execFileSync } from 'child_process';
|
|
14
15
|
import * as os from 'os';
|
|
15
16
|
import { getMcpDir, getUserMcpDir, getProjectAgentsDir, getVersionsDir, getUserAgentsDir } from './state.js';
|
|
16
17
|
import { getBinaryPath, getVersionHomePath } from './versions.js';
|
|
17
18
|
import { IS_WINDOWS, execFileShellSpec } from './platform/index.js';
|
|
18
|
-
import { AGENTS } from './agents.js';
|
|
19
|
+
import { AGENTS, getMcpConfigPathForHome, getProjectMcpConfigPath } from './agents.js';
|
|
19
20
|
import { isCapable } from './capabilities.js';
|
|
20
21
|
import { setGeminiAutoUpdateDisabled, updateGeminiSettings } from './gemini-settings.js';
|
|
21
22
|
/**
|
|
@@ -634,6 +635,225 @@ function installMcpToOpenCodeConfig(server, versionHome) {
|
|
|
634
635
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
635
636
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
636
637
|
}
|
|
638
|
+
/**
|
|
639
|
+
* Agents whose config file format is implemented by `writeMcpConfig`.
|
|
640
|
+
* Others are intentionally skipped until their schema is added.
|
|
641
|
+
*/
|
|
642
|
+
function writeMcpConfigSupportsAgent(agentId) {
|
|
643
|
+
switch (agentId) {
|
|
644
|
+
case 'claude':
|
|
645
|
+
case 'cursor':
|
|
646
|
+
case 'gemini':
|
|
647
|
+
case 'kimi':
|
|
648
|
+
case 'droid':
|
|
649
|
+
case 'forge':
|
|
650
|
+
case 'openclaw':
|
|
651
|
+
case 'codex':
|
|
652
|
+
case 'grok':
|
|
653
|
+
case 'opencode':
|
|
654
|
+
case 'hermes':
|
|
655
|
+
return true;
|
|
656
|
+
default:
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Serialize MCP servers into an agent-specific config file.
|
|
662
|
+
*
|
|
663
|
+
* `mode: 'overwrite'` replaces the whole MCP section (used for tool-managed
|
|
664
|
+
* version-home configs). `mode: 'merge'` updates/adds the provided server
|
|
665
|
+
* entries while preserving existing entries (used for project-level configs
|
|
666
|
+
* that users may hand-edit or populate via agent CLI commands).
|
|
667
|
+
*/
|
|
668
|
+
export function writeMcpConfig(agentId, configPath, servers, mode = 'overwrite') {
|
|
669
|
+
if (servers.length === 0) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
switch (agentId) {
|
|
673
|
+
case 'claude':
|
|
674
|
+
case 'cursor':
|
|
675
|
+
case 'gemini':
|
|
676
|
+
case 'kimi':
|
|
677
|
+
case 'droid':
|
|
678
|
+
case 'forge': {
|
|
679
|
+
let config = {};
|
|
680
|
+
if (fs.existsSync(configPath)) {
|
|
681
|
+
try {
|
|
682
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
683
|
+
}
|
|
684
|
+
catch {
|
|
685
|
+
config = {};
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
const mcpServers = mode === 'merge' && config.mcpServers && typeof config.mcpServers === 'object'
|
|
689
|
+
? { ...config.mcpServers }
|
|
690
|
+
: {};
|
|
691
|
+
for (const server of servers) {
|
|
692
|
+
if (server.transport === 'stdio') {
|
|
693
|
+
mcpServers[server.name] = {
|
|
694
|
+
command: server.command,
|
|
695
|
+
args: server.args || [],
|
|
696
|
+
env: server.env || {},
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
mcpServers[server.name] = {
|
|
701
|
+
url: server.url,
|
|
702
|
+
...(server.headers && { headers: server.headers }),
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
config.mcpServers = mcpServers;
|
|
707
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
708
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
case 'openclaw': {
|
|
712
|
+
let config = {};
|
|
713
|
+
if (fs.existsSync(configPath)) {
|
|
714
|
+
try {
|
|
715
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
716
|
+
}
|
|
717
|
+
catch {
|
|
718
|
+
config = {};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
if (!config.mcp || typeof config.mcp !== 'object') {
|
|
722
|
+
config.mcp = {};
|
|
723
|
+
}
|
|
724
|
+
const mcp = config.mcp;
|
|
725
|
+
const mcpServers = mode === 'merge' && mcp.servers && typeof mcp.servers === 'object'
|
|
726
|
+
? { ...mcp.servers }
|
|
727
|
+
: {};
|
|
728
|
+
for (const server of servers) {
|
|
729
|
+
if (server.transport === 'stdio') {
|
|
730
|
+
mcpServers[server.name] = {
|
|
731
|
+
command: server.command,
|
|
732
|
+
args: server.args || [],
|
|
733
|
+
env: server.env || {},
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
mcpServers[server.name] = {
|
|
738
|
+
url: server.url,
|
|
739
|
+
transport: server.transport,
|
|
740
|
+
...(server.headers && { headers: server.headers }),
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
mcp.servers = mcpServers;
|
|
745
|
+
config.mcp = mcp;
|
|
746
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
747
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
case 'codex':
|
|
751
|
+
case 'grok': {
|
|
752
|
+
let config = {};
|
|
753
|
+
if (fs.existsSync(configPath)) {
|
|
754
|
+
try {
|
|
755
|
+
config = TOML.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
config = {};
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const mcpServers = mode === 'merge' && config.mcp_servers && typeof config.mcp_servers === 'object'
|
|
762
|
+
? { ...config.mcp_servers }
|
|
763
|
+
: {};
|
|
764
|
+
for (const server of servers) {
|
|
765
|
+
if (server.transport === 'stdio') {
|
|
766
|
+
mcpServers[server.name] = {
|
|
767
|
+
command: server.command,
|
|
768
|
+
args: server.args || [],
|
|
769
|
+
...(server.env && { env: server.env }),
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
mcpServers[server.name] = {
|
|
774
|
+
url: server.url,
|
|
775
|
+
...(server.headers && { headers: server.headers }),
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
config.mcp_servers = mcpServers;
|
|
780
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
781
|
+
fs.writeFileSync(configPath, TOML.stringify(config), 'utf-8');
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
case 'opencode': {
|
|
785
|
+
let config = {};
|
|
786
|
+
if (fs.existsSync(configPath)) {
|
|
787
|
+
try {
|
|
788
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
789
|
+
const jsonContent = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
790
|
+
config = JSON.parse(jsonContent);
|
|
791
|
+
}
|
|
792
|
+
catch {
|
|
793
|
+
config = {};
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
const mcp = mode === 'merge' && config.mcp && typeof config.mcp === 'object'
|
|
797
|
+
? { ...config.mcp }
|
|
798
|
+
: {};
|
|
799
|
+
for (const server of servers) {
|
|
800
|
+
if (server.transport === 'stdio') {
|
|
801
|
+
const commandArray = [server.command, ...(server.args || [])];
|
|
802
|
+
mcp[server.name] = {
|
|
803
|
+
type: 'local',
|
|
804
|
+
command: commandArray,
|
|
805
|
+
...(server.env && { env: server.env }),
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
mcp[server.name] = {
|
|
810
|
+
type: 'remote',
|
|
811
|
+
url: server.url,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
config.mcp = mcp;
|
|
816
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
817
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
820
|
+
case 'hermes': {
|
|
821
|
+
let config = {};
|
|
822
|
+
if (fs.existsSync(configPath)) {
|
|
823
|
+
try {
|
|
824
|
+
const parsed = yaml.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
825
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
826
|
+
config = parsed;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
catch {
|
|
830
|
+
config = {};
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const mcpServers = mode === 'merge' && config.mcp_servers && typeof config.mcp_servers === 'object' && !Array.isArray(config.mcp_servers)
|
|
834
|
+
? { ...config.mcp_servers }
|
|
835
|
+
: {};
|
|
836
|
+
for (const server of servers) {
|
|
837
|
+
if (server.transport === 'stdio') {
|
|
838
|
+
mcpServers[server.name] = {
|
|
839
|
+
command: server.command,
|
|
840
|
+
args: server.args || [],
|
|
841
|
+
...(server.env && { env: server.env }),
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
mcpServers[server.name] = {
|
|
846
|
+
url: server.url,
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
config.mcp_servers = mcpServers;
|
|
851
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
852
|
+
fs.writeFileSync(configPath, yaml.stringify(config), 'utf-8');
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
637
857
|
/**
|
|
638
858
|
* Install MCP servers to an agent.
|
|
639
859
|
* For Claude/Codex: uses CLI commands (claude mcp add, codex mcp add)
|
|
@@ -658,47 +878,92 @@ export function installMcpServers(agentId, version, versionHome, mcpNames, optio
|
|
|
658
878
|
binaryPath += '.cmd';
|
|
659
879
|
}
|
|
660
880
|
for (const server of servers) {
|
|
881
|
+
let handled = false;
|
|
661
882
|
try {
|
|
662
883
|
if (agentId === 'claude') {
|
|
663
884
|
installMcpViaClaude(binaryPath, server, versionHome);
|
|
664
|
-
|
|
885
|
+
handled = true;
|
|
665
886
|
}
|
|
666
887
|
else if (agentId === 'codex') {
|
|
667
888
|
installMcpViaCodex(binaryPath, server, versionHome);
|
|
668
|
-
|
|
889
|
+
handled = true;
|
|
669
890
|
}
|
|
670
891
|
else if (agentId === 'gemini') {
|
|
671
892
|
installMcpToGeminiConfig(server, versionHome);
|
|
672
|
-
|
|
893
|
+
handled = true;
|
|
673
894
|
}
|
|
674
895
|
else if (agentId === 'cursor') {
|
|
675
896
|
installMcpToCursorConfig(server, versionHome);
|
|
676
|
-
|
|
897
|
+
handled = true;
|
|
677
898
|
}
|
|
678
899
|
else if (agentId === 'opencode') {
|
|
679
900
|
installMcpToOpenCodeConfig(server, versionHome);
|
|
680
|
-
|
|
901
|
+
handled = true;
|
|
902
|
+
}
|
|
903
|
+
else if (agentId === 'openclaw') {
|
|
904
|
+
// OpenClaw has no install CLI; write the JSON config directly.
|
|
905
|
+
// Use merge because this loop runs once per server.
|
|
906
|
+
const userConfigPath = getMcpConfigPathForHome(agentId, versionHome);
|
|
907
|
+
const writableServer = {
|
|
908
|
+
name: server.config.name,
|
|
909
|
+
transport: server.config.transport,
|
|
910
|
+
command: server.config.command,
|
|
911
|
+
args: server.config.args,
|
|
912
|
+
env: server.config.env,
|
|
913
|
+
url: server.config.url,
|
|
914
|
+
};
|
|
915
|
+
writeMcpConfig(agentId, userConfigPath, [writableServer], 'merge');
|
|
916
|
+
handled = true;
|
|
681
917
|
}
|
|
682
918
|
else if (agentId === 'grok') {
|
|
683
|
-
// Grok
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
|
|
919
|
+
// Grok has no working `grok mcp add` CLI, so write the TOML config
|
|
920
|
+
// directly into the version-home directory for all scopes.
|
|
921
|
+
// Use merge because this loop runs once per server.
|
|
922
|
+
const userConfigPath = getMcpConfigPathForHome(agentId, versionHome);
|
|
923
|
+
const writableServer = {
|
|
924
|
+
name: server.config.name,
|
|
925
|
+
transport: server.config.transport,
|
|
926
|
+
command: server.config.command,
|
|
927
|
+
args: server.config.args,
|
|
928
|
+
env: server.config.env,
|
|
929
|
+
url: server.config.url,
|
|
930
|
+
};
|
|
931
|
+
writeMcpConfig(agentId, userConfigPath, [writableServer], 'merge');
|
|
932
|
+
handled = true;
|
|
687
933
|
}
|
|
688
934
|
else if (agentId === 'kimi') {
|
|
689
935
|
installMcpToKimiConfig(server, versionHome);
|
|
690
|
-
|
|
936
|
+
handled = true;
|
|
691
937
|
}
|
|
692
938
|
else if (agentId === 'droid') {
|
|
693
939
|
installMcpToFactoryConfig(server, versionHome);
|
|
694
|
-
|
|
940
|
+
handled = true;
|
|
695
941
|
}
|
|
696
942
|
else if (agentId === 'hermes') {
|
|
697
943
|
installMcpToHermesConfig(server, versionHome);
|
|
698
|
-
|
|
944
|
+
handled = true;
|
|
699
945
|
}
|
|
700
946
|
else if (agentId === 'forge') {
|
|
701
947
|
installMcpToForgeConfig(server, versionHome);
|
|
948
|
+
handled = true;
|
|
949
|
+
}
|
|
950
|
+
// Project-layer servers also get merged into the agent's project-level
|
|
951
|
+
// config (e.g., .mcp.json, .codex/config.toml) so the CLI discovers them
|
|
952
|
+
// when run inside the repo.
|
|
953
|
+
if (server.scope === 'project' && options.cwd && writeMcpConfigSupportsAgent(agentId)) {
|
|
954
|
+
const projectConfigPath = getProjectMcpConfigPath(agentId, options.cwd);
|
|
955
|
+
const writableServer = {
|
|
956
|
+
name: server.config.name,
|
|
957
|
+
transport: server.config.transport,
|
|
958
|
+
command: server.config.command,
|
|
959
|
+
args: server.config.args,
|
|
960
|
+
env: server.config.env,
|
|
961
|
+
url: server.config.url,
|
|
962
|
+
};
|
|
963
|
+
writeMcpConfig(agentId, projectConfigPath, [writableServer], 'merge');
|
|
964
|
+
handled = true;
|
|
965
|
+
}
|
|
966
|
+
if (handled) {
|
|
702
967
|
applied.push(server.name);
|
|
703
968
|
}
|
|
704
969
|
}
|